Explorar el Código

Start of move from gaming core

Kajetan Johannes Hammerle hace 1 año
commit
91ba9b547c
Se han modificado 19 ficheros con 1100 adiciones y 0 borrados
  1. 67 0
      data/Array.h
  2. 56 0
      meson.build
  3. 10 0
      test/Main.cpp
  4. 56 0
      test/Test.cpp
  5. 40 0
      test/Test.h
  6. 26 0
      tests/ArrayTests.cpp
  7. 8 0
      tests/ArrayTests.h
  8. 248 0
      tests/StringTests.cpp
  9. 8 0
      tests/StringTests.h
  10. 22 0
      tests/UtilityTests.cpp
  11. 8 0
      tests/UtilityTests.h
  12. 21 0
      utils/Check.h
  13. 3 0
      utils/Logger.cpp
  14. 57 0
      utils/Logger.h
  15. 215 0
      utils/String.cpp
  16. 117 0
      utils/String.h
  17. 17 0
      utils/Types.h
  18. 38 0
      utils/Utility.cpp
  19. 83 0
      utils/Utility.h

+ 67 - 0
data/Array.h

@@ -0,0 +1,67 @@
+#ifndef CORE_ARRAY_H
+#define CORE_ARRAY_H
+
+#include "utils/String.h"
+
+namespace Core {
+    template<typename T, int N>
+    class Array final {
+        static_assert(N > 0, "Array size must be positive");
+        T data[static_cast<unsigned int>(N)];
+
+    public:
+        Array() = default;
+
+        Array(const T& t) {
+            fill(t);
+        }
+
+        void fill(const T& t) {
+            for(int i = 0; i < N; i++) {
+                data[i] = t;
+            }
+        }
+
+        T& operator[](int index) {
+            return data[index];
+        }
+
+        const T& operator[](int index) const {
+            return data[index];
+        }
+
+        T* begin() {
+            return data;
+        }
+
+        T* end() {
+            return data + N;
+        }
+
+        const T* begin() const {
+            return data;
+        }
+
+        const T* end() const {
+            return data + N;
+        }
+
+        constexpr int getLength() const {
+            return N;
+        }
+
+        void toString(String& s) const {
+            s.append("[");
+            for(int i = 0; i < N - 1; i++) {
+                s.append(data[i]);
+                s.append(", ");
+            }
+            if(N > 0) {
+                s.append(data[N - 1]);
+            }
+            s.append("]");
+        }
+    };
+}
+
+#endif

+ 56 - 0
meson.build

@@ -0,0 +1,56 @@
+project('core', 'cpp', default_options : ['cpp_std=c++2a'])
+
+src = [
+    'utils/String.cpp',
+    'utils/Logger.cpp',
+    'utils/Utility.cpp',
+]
+
+src_tests = [
+    'test/Main.cpp',
+    'test/Test.cpp',
+    'tests/ArrayTests.cpp',
+    'tests/StringTests.cpp',
+    'tests/UtilityTests.cpp',
+]
+
+compiler = meson.get_compiler('cpp')
+error_args = compiler.get_supported_arguments([
+    '-Wcast-align=strict', '-pedantic', '-Wmissing-declarations', '-Wdate-time', 
+    '-Winit-self', '-Woverlength-strings', '-Wsign-promo', '-Wnon-virtual-dtor', 
+    '-Wconversion', '-Woverloaded-virtual', '-Wdeprecated-enum-enum-conversion', 
+    '-Wdisabled-optimization', '-Winvalid-imported-macros', '-Wduplicated-cond', 
+    '-Wdeprecated-enum-float-conversion', '-Wduplicated-branches', '-Wformat=2', 
+    '-Wmissing-braces', '-Wsuggest-override', '-Wcast-qual', '-Wbidi-chars=any', 
+    '-Wzero-as-null-pointer-constant', '-pedantic-errors', '-Wnull-dereference', 
+    '-Wformat-signedness', '-Wfloat-equal', '-Wvolatile', '-Wctor-dtor-privacy', 
+    '-Winfinite-recursion', '-Wshift-overflow=2', '-Wmultichar', '-Walloc-zero', 
+    '-Wcomma-subscript', '-Wold-style-cast', '-Wwrite-strings', '-Wswitch-enum', 
+    '-Wredundant-decls', '-Werror', '-Wsign-conversion', '-Walloca', '-Wshadow',
+    '-Winvalid-pch', '-Wdeprecated-copy-dtor', '-Wundef', '-Wdouble-promotion',
+    '-Warith-conversion', '-Wextra', '-Wtrivial-auto-var-init', '-Wlogical-op', 
+    '-Wall', '-Wenum-conversion'
+])
+compile_args = compiler.get_supported_arguments([
+    '-nostdinc++', '-fno-exceptions', '-fno-rtti', '-fno-threadsafe-statics'
+])
+link_args = compiler.get_supported_arguments([
+    '-nodefaultlibs', '-lc'
+])
+
+core_include = [include_directories('.')]
+core = static_library('core', 
+    sources: src,
+    include_directories: core_include,
+    cpp_args: error_args + compile_args,
+    link_args: link_args)
+
+core_dep = declare_dependency(
+    include_directories: core_include,
+    link_with: core)
+
+executable('tests', 
+    sources: src_tests,
+    dependencies: core_dep,
+    cpp_args: error_args + compile_args + ['-DLOG_LEVEL=4'],
+    link_args: link_args)

+ 10 - 0
test/Main.cpp

@@ -0,0 +1,10 @@
+#include "tests/ArrayTests.h"
+#include "tests/StringTests.h"
+#include "tests/UtilityTests.h"
+
+int main() {
+    Core::ArrayTests::test();
+    Core::StringTests::test();
+    Core::UtilityTests::test();
+    return 0;
+}

+ 56 - 0
test/Test.cpp

@@ -0,0 +1,56 @@
+#include "test/Test.h"
+
+Core::Test::Test(const String& name_) : tests(0), successTests(0), name(name_) {
+}
+
+void Core::Test::finalize() {
+    if(successTests == tests) {
+        LOG_DEBUG("# Tests: # / # succeeded", name, successTests, tests);
+    } else {
+        LOG_ERROR("# Tests: # / # succeeded", name, successTests, tests);
+    }
+    tests = 0;
+    successTests = 0;
+}
+
+void Core::Test::checkEqual(float wanted, float actual, const char* text) {
+    checkFloat(wanted, actual, 0.0000001f, text);
+}
+
+void Core::Test::checkFloat(float wanted, float actual, float error,
+                            const char* text) {
+    tests++;
+    float diff = wanted - actual;
+    diff = diff < 0.0f ? -diff : diff;
+    if(diff < error) {
+        successTests++;
+    } else {
+        (void)text;
+        LOG_ERROR("# Test #: # - expected '#' got '#'", name, tests, text,
+                  wanted, actual)
+    }
+}
+
+void Core::Test::checkTrue(bool actual, const char* text) {
+    checkEqual(true, actual, text);
+}
+
+void Core::Test::checkFalse(bool actual, const char* text) {
+    checkEqual(false, actual, text);
+}
+
+void Core::Test::checkUnsigned8(u8 wanted, u8 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}
+
+void Core::Test::checkUnsigned16(u16 wanted, u16 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}
+
+void Core::Test::checkSigned8(i8 wanted, i8 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}
+
+void Core::Test::checkSigned16(i16 wanted, i16 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}

+ 40 - 0
test/Test.h

@@ -0,0 +1,40 @@
+#ifndef CORE_TEST_H
+#define CORE_TEST_H
+
+#include "utils/Logger.h"
+
+namespace Core {
+    class Test final {
+        int tests;
+        int successTests;
+        String name;
+
+    public:
+        Test(const String& name);
+        void finalize();
+
+        template<typename T>
+        void checkEqual(const T& wanted, const T& actual, const char* text) {
+            tests++;
+            if(wanted == actual) {
+                successTests++;
+            } else {
+                (void)text;
+                LOG_ERROR("# Test #: # - expected '#' got '#'", name, tests,
+                          text, wanted, actual)
+            }
+        }
+
+        void checkEqual(float wanted, float actual, const char* text);
+        void checkFloat(float wanted, float actual, float error,
+                        const char* text);
+        void checkTrue(bool actual, const char* text);
+        void checkFalse(bool actual, const char* text);
+        void checkUnsigned8(u8 wanted, u8 actual, const char* text);
+        void checkUnsigned16(u16 wanted, u16 actual, const char* text);
+        void checkSigned8(i8 wanted, i8 actual, const char* text);
+        void checkSigned16(i16 wanted, i16 actual, const char* text);
+    };
+}
+
+#endif

+ 26 - 0
tests/ArrayTests.cpp

@@ -0,0 +1,26 @@
+#include "tests/ArrayTests.h"
+
+#include "data/Array.h"
+#include "test/Test.h"
+
+static void testToString1(Core::Test& test) {
+    Core::Array<int, 3> a;
+    a[0] = 1;
+    a[1] = 243;
+    a[2] = -423;
+    test.checkEqual(Core::String("[1, 243, -423]"), Core::String(a),
+                    "to string 1");
+}
+
+static void testToString2(Core::Test& test) {
+    Core::Array<int, 1> a;
+    a[0] = 1;
+    test.checkEqual(Core::String("[1]"), Core::String(a), "to string 2");
+}
+
+void Core::ArrayTests::test() {
+    Core::Test test("Array");
+    testToString1(test);
+    testToString2(test);
+    test.finalize();
+}

+ 8 - 0
tests/ArrayTests.h

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

+ 248 - 0
tests/StringTests.cpp

@@ -0,0 +1,248 @@
+#include "tests/StringTests.h"
+
+#include "test/Test.h"
+#include "utils/String.h"
+
+// #include "data/HashMap.h"
+
+static void testEquality(Core::Test& test) {
+    Core::String s("test");
+    test.checkTrue(s == "test", "equality with c-string");
+    test.checkTrue(s == Core::String("test"), "equality with another string");
+    test.checkTrue("test" == s, "inverse equality with c-string");
+    test.checkTrue(Core::String("test") == s,
+                   "inverse equality with another string");
+    test.checkTrue(s == s, "equality with itself");
+}
+
+static void testInequality(Core::Test& test) {
+    Core::String s("test");
+    test.checkFalse(s != "test", "inequality with c-string");
+    test.checkFalse(s != Core::String("test"),
+                    "inequality with another string");
+    test.checkFalse("test" != s, "inverse inequality with c-string");
+    test.checkFalse(Core::String("test") != s,
+                    "inverse inequality with another string");
+    test.checkFalse(s != s, "inequality with itself");
+}
+
+static void testStringAppend(Core::Test& test) {
+    Core::String s("test");
+    s.append("22").append("333").append("4444");
+    test.checkEqual(Core::String("test223334444"), s, "multiple appends");
+}
+
+static void testCharacters(Core::Test& test) {
+    Core::String s("test");
+    test.checkEqual(static_cast<u32>('t'), s[0], "character read 1");
+    test.checkEqual(static_cast<u32>('e'), s[1], "character read 2");
+    test.checkEqual(static_cast<u32>('s'), s[2], "character read 3");
+    test.checkEqual(static_cast<u32>('t'), s[3], "character read 4");
+}
+
+static void testLength(Core::Test& test) {
+    Core::String s("test");
+    test.checkEqual(4, s.getLength(), "length 1");
+    s.append("aaa");
+    test.checkEqual(7, s.getLength(), "length 2");
+}
+
+static void testChar(Core::Test& test) {
+    Core::String s("test");
+    for(char i = 'a'; i < 'd'; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("testabc"), s, "char append");
+}
+
+static void testSignedChar(Core::Test& test) {
+    Core::String s("test");
+    for(signed char i = 'b'; i < 'e'; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("testbcd"), s, "signed char append");
+}
+
+static void testUnsignedChar(Core::Test& test) {
+    Core::String s("test");
+    for(unsigned char i = 'c'; i < 'f'; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("testcde"), s, "unsigned char append");
+}
+
+static void testSignedShort(Core::Test& test) {
+    Core::String s("test");
+    for(signed short i = 100; i < 103; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test100101102"), s, "signed short append");
+}
+
+static void testUnsignedShort(Core::Test& test) {
+    Core::String s("test");
+    for(unsigned short i = 101; i < 104; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test101102103"), s, "unsigned short append");
+}
+
+static void testSignedInt(Core::Test& test) {
+    Core::String s("test");
+    for(signed int i = 102; i < 105; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test102103104"), s, "signed int append");
+}
+
+static void testUnsignedInt(Core::Test& test) {
+    Core::String s("test");
+    for(unsigned int i = 103; i < 106; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test103104105"), s, "unsigned int append");
+}
+
+static void testSignedLong(Core::Test& test) {
+    Core::String s("test");
+    for(signed long i = 104; i < 107; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test104105106"), s, "signed long append");
+}
+
+static void testUnsignedLong(Core::Test& test) {
+    Core::String s("test");
+    for(unsigned long i = 105; i < 108; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test105106107"), s, "unsigned long append");
+}
+
+static void testSignedLongLong(Core::Test& test) {
+    Core::String s("test");
+    for(signed long long i = 106; i < 109; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test106107108"), s,
+                    "signed long long append");
+}
+
+static void testUnsignedLongLong(Core::Test& test) {
+    Core::String s("test");
+    for(unsigned long long i = 107; i < 110; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test107108109"), s,
+                    "unsigned long long append");
+}
+
+static void testFloat(Core::Test& test) {
+    Core::String s("test");
+    for(float i = 108; i < 111; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test108.00109.00110.00"), s, "float append");
+}
+
+static void testDouble(Core::Test& test) {
+    Core::String s("test");
+    for(double i = 109; i < 112; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test109.00110.00111.00"), s, "double append");
+}
+
+static void testLongDouble(Core::Test& test) {
+    Core::String s("test");
+    for(long double i = 110; i < 113; i++) {
+        s.append(i);
+    }
+    test.checkEqual(Core::String("test110.00111.00112.00"), s,
+                    "long double append");
+}
+
+static void testBool(Core::Test& test) {
+    Core::String s("test");
+    s.append(true).append(false).append(true);
+    test.checkEqual(Core::String("testtruefalsetrue"), s, "bool append");
+}
+
+static void testUnicode(Core::Test& test) {
+    Core::String s;
+    s.appendUnicode('\u0040');
+    s.appendUnicode(L'\u0400');
+    s.appendUnicode(L'\u8000');
+    s.appendUnicode(U'\U00100000');
+    test.checkEqual(Core::String("\u0040\u0400\u8000\U00100000"), s,
+                    "unicode append");
+}
+
+static void testClear(Core::Test& test) {
+    Core::String s("test");
+    s.append(1234).clear().append("wusi").append("1234");
+    test.checkEqual(Core::String("wusi1234"), s, "clear");
+}
+
+static void testHashCode(Core::Test& test) {
+    Core::String s;
+    s.append("a").append("bc").append(20).append(25.5f).append(true);
+    test.checkEqual(Core::String("abc2025.50true").hashCode(), s.hashCode(),
+                    "string modification recalculates hash 1");
+    s.clear();
+    test.checkEqual(Core::String().hashCode(), s.hashCode(),
+                    "string modification recalculates hash 2");
+}
+
+static void testAsHashMapKey(Core::Test& test) {
+    (void)test;
+    /*HashMap<String, int> map;
+    map.add(String("wusi"), 3)
+        .add(String("hiThere"), 7)
+        .add(String("baum123"), 5);
+
+    int* a = map.search(String("wusi"));
+    int* b = map.search(String("hiThere"));
+    int* c = map.search(String("baum123"));
+    int* d = map.search(String("423hifd"));
+
+    test.checkEqual(true, a != nullptr, "strings works as hash key 1");
+    test.checkEqual(true, b != nullptr, "strings works as hash key 2");
+    test.checkEqual(true, c != nullptr, "strings works as hash key 3");
+    test.checkEqual(true, d == nullptr, "strings works as hash key 4");
+
+    if(a != nullptr && b != nullptr && c != nullptr) {
+        test.checkEqual(3, *a, "strings works as hash key 1");
+        test.checkEqual(7, *b, "strings works as hash key 2");
+        test.checkEqual(5, *c, "strings works as hash key 3");
+    }*/
+}
+
+void Core::StringTests::test() {
+    Core::Test test("String");
+    testEquality(test);
+    testInequality(test);
+    testStringAppend(test);
+    testCharacters(test);
+    testLength(test);
+    testChar(test);
+    testSignedChar(test);
+    testUnsignedChar(test);
+    testSignedShort(test);
+    testUnsignedShort(test);
+    testSignedInt(test);
+    testUnsignedInt(test);
+    testSignedLong(test);
+    testUnsignedLong(test);
+    testSignedLongLong(test);
+    testUnsignedLongLong(test);
+    testFloat(test);
+    testDouble(test);
+    testLongDouble(test);
+    testBool(test);
+    testUnicode(test);
+    testClear(test);
+    testHashCode(test);
+    testAsHashMapKey(test);
+    test.finalize();
+}

+ 8 - 0
tests/StringTests.h

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

+ 22 - 0
tests/UtilityTests.cpp

@@ -0,0 +1,22 @@
+#include "tests/UtilityTests.h"
+
+#include "test/Test.h"
+#include "utils/Utility.h"
+
+static void testPopCount(Core::Test& test) {
+    test.checkEqual(4, Core::popCount(0xF), "pop count 1");
+    test.checkEqual(0, Core::popCount(0x0), "pop count 2");
+    test.checkEqual(2, Core::popCount(0x6), "pop count 3");
+    test.checkEqual(7, Core::popCount(0x7F), "pop count 4");
+    test.checkEqual(3, Core::popCount(0x2A), "pop count 5");
+    test.checkEqual(32, Core::popCount(0xFFFFFFFF), "pop count 6");
+    test.checkEqual(64, Core::popCount(0xFFFFFFFFFFFFFFFFL), "pop count 7");
+    test.checkEqual(44, Core::popCount(0xFFFF0FFFFFFF), "pop count 8");
+    test.checkEqual(32, Core::popCount(-1), "pop count 9");
+}
+
+void Core::UtilityTests::test() {
+    Core::Test test("Utility");
+    testPopCount(test);
+    test.finalize();
+}

+ 8 - 0
tests/UtilityTests.h

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

+ 21 - 0
utils/Check.h

@@ -0,0 +1,21 @@
+#ifndef CORE_CHECK_H
+#define CORE_CHECK_H
+
+#if defined(__cplusplus) && __cplusplus > 201700L
+#define check_return [[nodiscard]]
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ > 202300L
+#define check_return [[nodiscard]]
+#elif defined(__GNUC__)
+#define check_return __attribute__((warn_unused_result))
+#else
+#error "please add a 'check_return' option"
+#endif
+
+#if defined(__GNUC__)
+#define check_format(format_index, arg_start_index)                            \
+    __attribute__((format(printf, format_index, arg_start_index)))
+#else
+#error "please add a 'check_format' option"
+#endif
+
+#endif

+ 3 - 0
utils/Logger.cpp

@@ -0,0 +1,3 @@
+#include "utils/Logger.h"
+
+Core::Logger::Level Core::Logger::level = Core::Logger::Level::DEBUG;

+ 57 - 0
utils/Logger.h

@@ -0,0 +1,57 @@
+#ifndef CORE_LOGGER_H
+#define CORE_LOGGER_H
+
+#include <stdio.h>
+
+#include "utils/String.h"
+
+namespace Core::Logger {
+    enum class Level { ERROR, WARNING, INFO, DEBUG };
+    extern Level level;
+}
+
+#if defined(LOG_LEVEL) && LOG_LEVEL >= 1
+#define LOG_ERROR(form, ...)                                                   \
+    if(Core::Logger::level >= Core::Logger::Level::ERROR) {                    \
+        printf("\33[1;31m[ERROR] ");                                           \
+        Core::String::format(form, __VA_ARGS__).print();                       \
+        printf("\33[39;49m\n");                                                \
+    }
+#else
+#define LOG_ERROR(form, ...)
+#endif
+
+#if defined(LOG_LEVEL) && LOG_LEVEL >= 2
+#define LOG_WARNING(form, ...)                                                 \
+    if(Core::Logger::level >= Core::Logger::Level::WARNING) {                  \
+        printf("\33[1;33m[WARNING] ");                                         \
+        Core::String::format(form, __VA_ARGS__).print();                       \
+        printf("\33[39;49m\n");                                                \
+    }
+#else
+#define LOG_WARNING(form, ...)
+#endif
+
+#if defined(LOG_LEVEL) && LOG_LEVEL >= 3
+#define LOG_INFO(form, ...)                                                    \
+    if(Core::Logger::level >= Core::Logger::Level::INFO) {                     \
+        printf("\33[1;37m[INFO] ");                                            \
+        Core::String::format(form, __VA_ARGS__).print();                       \
+        printf("\33[39;49m\n");                                                \
+    }
+#else
+#define LOG_INFO(form, ...)
+#endif
+
+#if defined(LOG_LEVEL) && LOG_LEVEL >= 4
+#define LOG_DEBUG(form, ...)                                                   \
+    if(Core::Logger::level >= Core::Logger::Level::DEBUG) {                    \
+        printf("\33[1;32m[DEBUG] ");                                           \
+        Core::String::format(form, __VA_ARGS__).print();                       \
+        printf("\33[39;49m\n");                                                \
+    }
+#else
+#define LOG_DEBUG(form, ...)
+#endif
+
+#endif

+ 215 - 0
utils/String.cpp

@@ -0,0 +1,215 @@
+#include "utils/String.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+Core::String::String() : length(0), hash(0) {
+    data[0] = '\0';
+}
+
+bool Core::String::operator==(const char* s) const {
+    return *this == String(s);
+}
+
+bool Core::String::operator==(const String& other) const {
+    if(length != other.length) {
+        return false;
+    }
+    for(int i = 0; i < length; i++) {
+        if(data[i] != other[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool Core::String::operator!=(const char* s) const {
+    return !((*this) == s);
+}
+
+bool Core::String::operator!=(const String& other) const {
+    return !((*this) == other);
+}
+
+u32 Core::String::operator[](int index) const {
+    return data[index];
+}
+
+int Core::String::getLength() const {
+    return length;
+}
+
+Core::String& Core::String::append(char c) {
+    return appendUnicode(static_cast<u32>(c < 0 ? '?' : c));
+}
+
+Core::String& Core::String::append(signed char c) {
+    return appendUnicode(static_cast<u32>(c < 0 ? '?' : c));
+}
+
+Core::String& Core::String::append(unsigned char c) {
+    return appendUnicode(c);
+}
+
+static u32 read(const char*& s) {
+    if(*s == '\0') {
+        return 0;
+    }
+    return static_cast<u32>(*(s++));
+}
+
+Core::String& Core::String::append(const char* s) {
+    while(true) {
+        u32 u = read(s);
+        if(u == 0) {
+            break;
+        } else if((u & 0xE0) == 0xC0) {
+            u = ((u & 0x1F) << 6) | (read(s) & 0x3F);
+        } else if((u & 0xF0) == 0xE0) {
+            u = ((u & 0xF) << 12) | ((read(s) & 0x3F) << 6);
+            u |= read(s) & 0x3F;
+        } else if((u & 0xF8) == 0xF0) {
+            u = ((u & 0x07) << 18) | ((read(s) & 0x3F) << 12);
+            u |= (read(s) & 0x3F) << 6;
+            u |= read(s) & 0x3F;
+        }
+        appendUnicode(u);
+    }
+    return *this;
+}
+
+Core::String& Core::String::append(const signed char* s) {
+    return append(reinterpret_cast<const char*>(s));
+}
+
+Core::String& Core::String::append(const unsigned char* s) {
+    return append(reinterpret_cast<const char*>(s));
+}
+
+Core::String& Core::String::append(signed short s) {
+    return appendFormat("%hd", s);
+}
+
+Core::String& Core::String::append(unsigned short s) {
+    return appendFormat("%hu", s);
+}
+
+Core::String& Core::String::append(signed int i) {
+    return appendFormat("%d", i);
+}
+
+Core::String& Core::String::append(unsigned int i) {
+    return appendFormat("%u", i);
+}
+
+Core::String& Core::String::append(signed long l) {
+    return appendFormat("%ld", l);
+}
+
+Core::String& Core::String::append(unsigned long l) {
+    return appendFormat("%lu", l);
+}
+
+Core::String& Core::String::append(signed long long ll) {
+    return appendFormat("%lld", ll);
+}
+
+Core::String& Core::String::append(unsigned long long ll) {
+    return appendFormat("%llu", ll);
+}
+
+Core::String& Core::String::append(float f) {
+    return appendFormat("%.2f", static_cast<double>(f));
+}
+
+Core::String& Core::String::append(double d) {
+    return appendFormat("%.2f", d);
+}
+
+Core::String& Core::String::append(long double ld) {
+    return appendFormat("%.2Lf", ld);
+}
+
+Core::String& Core::String::append(bool b) {
+    return b ? append("true") : append("false");
+}
+
+Core::String& Core::String::appendUnicode(u32 c) {
+    if(length < DATA_LENGTH) {
+        data[length++] = c;
+        addToHash(c);
+    }
+    return *this;
+}
+
+check_format(2, 3) Core::String& Core::String::appendFormat(const char* format,
+                                                            ...) {
+    constexpr int BUFFER_SIZE = 64;
+    char buffer[BUFFER_SIZE];
+
+    va_list args;
+    va_start(args, format);
+    vsnprintf(buffer, BUFFER_SIZE, format, args);
+    va_end(args);
+
+    append(static_cast<const char*>(buffer));
+    return *this;
+}
+
+void Core::String::toString(String& s) const {
+    if(this == &s) {
+        return;
+    }
+    for(int i = 0; i < length; i++) {
+        s.appendUnicode(data[i]);
+    }
+}
+
+Core::String& Core::String::clear() {
+    length = 0;
+    hash = 0;
+    data[0] = '\0';
+    return *this;
+}
+
+Hash Core::String::hashCode() const {
+    return hash;
+}
+
+void Core::String::print() const {
+    for(int i = 0; i < length; i++) {
+        u32 c = data[i];
+        if(c < (1 << 7)) {
+            putchar(static_cast<int>(c & 0x7F));
+        } else if(c < (1 << 11)) {
+            putchar(static_cast<int>(((c >> 6) & 0x1F) | 0xC0));
+            putchar(static_cast<int>(((c >> 0) & 0x3F) | 0x80));
+        } else if(c < (1 << 16)) {
+            putchar(static_cast<int>(((c >> 12) & 0x0F) | 0xE0));
+            putchar(static_cast<int>(((c >> 6) & 0x3F) | 0x80));
+            putchar(static_cast<int>(((c >> 0) & 0x3F) | 0x80));
+        } else if(c < (1 << 21)) {
+            putchar(static_cast<int>(((c >> 18) & 0x07) | 0xF0));
+            putchar(static_cast<int>(((c >> 12) & 0x3F) | 0x80));
+            putchar(static_cast<int>(((c >> 6) & 0x3F) | 0x80));
+            putchar(static_cast<int>(((c >> 0) & 0x3F) | 0x80));
+        }
+    }
+}
+
+void Core::String::printLine() const {
+    print();
+    putchar('\n');
+}
+
+void Core::String::addToHash(u32 u) {
+    hash = static_cast<Hash>(2120251889) * hash + static_cast<Hash>(u);
+}
+
+bool operator==(const char* cs, const Core::String& s) {
+    return s == cs;
+}
+
+bool operator!=(const char* cs, const Core::String& s) {
+    return s != cs;
+}

+ 117 - 0
utils/String.h

@@ -0,0 +1,117 @@
+#ifndef CORE_STRING_H
+#define CORE_STRING_H
+
+#include "utils/Check.h"
+#include "utils/Types.h"
+#include "utils/Utility.h"
+
+namespace Core {
+    class String final {
+        int length;
+        Hash hash;
+        static constexpr int DATA_LENGTH =
+            (1024 - (sizeof(length) >= sizeof(hash) ? sizeof(length) * 2
+                                                    : sizeof(hash) * 2)) /
+            sizeof(u32);
+        u32 data[DATA_LENGTH];
+
+    public:
+        String();
+
+        template<typename T>
+        String(const T& t) : String() {
+            append(t);
+        }
+
+        bool operator==(const char* s) const;
+        bool operator==(const String& other) const;
+        bool operator!=(const char* s) const;
+        bool operator!=(const String& other) const;
+        u32 operator[](int index) const;
+
+        int getLength() const;
+
+        String& append(char c);
+        String& append(signed char c);
+        String& append(unsigned char c);
+
+        String& append(const char* s);
+        String& append(const signed char* s);
+        String& append(const unsigned char* s);
+
+        String& append(signed short s);
+        String& append(unsigned short s);
+
+        String& append(signed int i);
+        String& append(unsigned int i);
+
+        String& append(signed long l);
+        String& append(unsigned long l);
+
+        String& append(signed long long ll);
+        String& append(unsigned long long ll);
+
+        String& append(float f);
+        String& append(double d);
+        String& append(long double ld);
+
+        String& append(bool b);
+
+        String& appendUnicode(u32 c);
+        check_format(2, 3) String& appendFormat(const char* format, ...);
+
+        template<typename T>
+        String& append(const T& t) {
+            t.toString(*this);
+            return *this;
+        }
+
+        void toString(String& s) const;
+
+        String& clear();
+        Hash hashCode() const;
+        void print() const;
+        void printLine() const;
+
+        template<typename... Args>
+        static String format(const String& format, Args&&... args) {
+            String s;
+            formatBuffer(s, 0, format, Core::forward<Args>(args)...);
+            return s;
+        }
+
+    private:
+        void addToHash(u32 u);
+
+        template<typename T, typename... Args>
+        static void formatBuffer(String& s, int index, const String& format,
+                                 const T& t, Args&&... args) {
+            while(index < format.getLength()) {
+                u32 u = format[index++];
+                if(u == '#') {
+                    if(index < format.getLength() && format[index] != '#') {
+                        break;
+                    }
+                    index++;
+                }
+                s.appendUnicode(u);
+            }
+            s.append(t);
+            formatBuffer(s, index, format, Core::forward<Args>(args)...);
+        }
+
+        static void formatBuffer(String& s, int index, const String& format) {
+            while(index < format.getLength()) {
+                s.appendUnicode(format[index++]);
+            }
+        }
+    };
+}
+
+static_assert(sizeof(Core::String) == 1024,
+              "String should have a size of 1024 byte");
+
+bool operator==(const char* cs, const Core::String& s);
+bool operator!=(const char* cs, const Core::String& s);
+
+#endif

+ 17 - 0
utils/Types.h

@@ -0,0 +1,17 @@
+#ifndef CORE_TYPES_H
+#define CORE_TYPES_H
+
+#include <inttypes.h>
+
+typedef int64_t i64;
+typedef int32_t i32;
+typedef int16_t i16;
+typedef int8_t i8;
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+typedef u32 Hash;
+
+#endif

+ 38 - 0
utils/Utility.cpp

@@ -0,0 +1,38 @@
+#include "utils/Utility.h"
+
+/*
+void* operator new(size_t bytes) noexcept {
+    return malloc(bytes);
+}
+
+void* operator new[](size_t bytes) noexcept {
+    return malloc(bytes);
+}
+
+void operator delete(void* p) noexcept {
+    free(p);
+}
+
+void operator delete[](void* p) noexcept {
+    free(p);
+}
+
+void operator delete(void* p, size_t bytes) noexcept {
+    (void)bytes;
+    free(p);
+}
+
+void operator delete[](void* p, size_t bytes) noexcept {
+    (void)bytes;
+    free(p);
+}
+
+void* operator new(size_t bytes, void* p) noexcept {
+    (void)bytes;
+    return p;
+}
+
+void* operator new[](size_t bytes, void* p) noexcept {
+    (void)bytes;
+    return p;
+}*/

+ 83 - 0
utils/Utility.h

@@ -0,0 +1,83 @@
+#ifndef CORE_UTILITY_H
+#define CORE_UTILITY_H
+
+// #include <stdlib.h>
+
+namespace Core {
+    namespace Internal {
+        template<typename T>
+        struct RemoveReferenceBase {
+            typedef T t;
+        };
+
+        template<typename T>
+        struct RemoveReferenceBase<T&> {
+            typedef T t;
+        };
+
+        template<typename T>
+        struct RemoveReferenceBase<T&&> {
+            typedef T t;
+        };
+
+        template<typename A, typename B>
+        struct IsSameBase {
+            static constexpr bool value = false;
+        };
+
+        template<typename T>
+        struct IsSameBase<T, T> {
+            static constexpr bool value = true;
+        };
+    }
+
+    template<typename T>
+    using RemoveReference = typename Internal::RemoveReferenceBase<T>::t;
+
+    template<typename T, typename U>
+    constexpr bool IsSame = Internal::IsSameBase<T, U>::value;
+
+    template<typename T>
+    constexpr RemoveReference<T>&& move(T&& t) {
+        return static_cast<RemoveReference<T>&&>(t);
+    }
+
+    template<typename T>
+    constexpr T&& forward(RemoveReference<T>& t) {
+        return static_cast<T&&>(t);
+    }
+
+    template<typename T>
+    constexpr T&& forward(RemoveReference<T>&& t) {
+        return static_cast<T&&>(t);
+    }
+
+    template<typename T>
+    void swap(T& a, T& b) {
+        T tmp = Core::move(a);
+        a = Core::move(b);
+        b = Core::move(tmp);
+    }
+
+    template<typename T>
+    int popCount(const T& t) {
+        static constexpr int map[16] = {0, 1, 1, 2, 1, 2, 2, 3,
+                                        1, 2, 2, 3, 2, 3, 3, 4};
+        int sum = 0;
+        for(int i = 0; i < static_cast<int>(sizeof(T) * 8); i += 4) {
+            sum += map[(t >> i) & 0xF];
+        }
+        return sum;
+    }
+}
+
+// void* operator new(size_t bytes) noexcept;
+// void* operator new[](size_t bytes) noexcept;
+// void operator delete(void* p) noexcept;
+// void operator delete[](void* p) noexcept;
+// void operator delete(void* p, size_t bytes) noexcept;
+// void operator delete[](void* p, size_t bytes) noexcept;
+// void* operator new(size_t bytes, void* p) noexcept;
+// void* operator new[](size_t bytes, void* p) noexcept;
+
+#endif