Quellcode durchsuchen

unlikely errors, improved string tests and array handling

Kajetan Johannes Hammerle vor 11 Monaten
Ursprung
Commit
c70ceb2050
8 geänderte Dateien mit 802 neuen und 344 gelöschten Zeilen
  1. 4 0
      math/Math.h
  2. 15 1
      math/Vector.h
  3. 5 19
      test/Test.cpp
  4. 18 13
      test/Test.h
  5. 705 283
      tests/ArrayStringTests.cpp
  6. 20 0
      tests/MathTests.cpp
  7. 24 27
      utils/ArrayString.h
  8. 11 1
      utils/Utility.h

+ 4 - 0
math/Math.h

@@ -9,6 +9,10 @@ namespace Core::Math {
         return a * (1.0f - f) + b * f;
     }
 
+    constexpr bool isPowerOf2(int i) {
+        return (i & (i - 1)) == 0;
+    }
+
     constexpr int roundUpLog2(int i) {
         if(i <= 0) {
             return 0;

+ 15 - 1
math/Vector.h

@@ -6,7 +6,7 @@
 
 namespace Core {
     template<int N, typename T>
-    class Vector final {
+    class alignas(sizeof(T) * (Math::isPowerOf2(N) ? N : 1)) Vector final {
         static_assert(N > 0, "Vector size must be positive");
         T values[static_cast<unsigned int>(N)];
 
@@ -187,10 +187,24 @@ namespace Core {
     using Vector3 = Vector<3, float>;
     using Vector2 = Vector<2, float>;
 
+    static_assert(alignof(Vector4) == sizeof(float) * 4,
+                  "invalid Vector4 alignment");
+    static_assert(alignof(Vector3) == sizeof(float),
+                  "invalid Vector3 alignment");
+    static_assert(alignof(Vector2) == sizeof(float) * 2,
+                  "invalid Vector2 alignment");
+
     using IntVector4 = Vector<4, int>;
     using IntVector3 = Vector<3, int>;
     using IntVector2 = Vector<2, int>;
 
+    static_assert(alignof(IntVector4) == sizeof(int) * 4,
+                  "invalid IntVector4 alignment");
+    static_assert(alignof(IntVector3) == sizeof(int),
+                  "invalid IntVector3 alignment");
+    static_assert(alignof(IntVector2) == sizeof(int) * 2,
+                  "invalid IntVector2 alignment");
+
     template<>
     Vector3& Vector3::setAngles(float lengthAngle, float widthAngle);
 

+ 5 - 19
test/Test.cpp

@@ -3,29 +3,15 @@
 Core::HashMap<Core::Test::Internal::FileName, Core::Test::Internal::Result>
     Core::Test::Internal::results;
 
+void Core::Test::Internal::warn(const char* file, int line, Error e) {
+    CORE_LOG_WARNING("#:# | ", Core::getFileName(file), line, e);
+}
+
 bool Core::Test::Internal::checkFloat(const char* file, int line, float wanted,
                                       float actual, float error) {
-    Error e = Error::NONE;
-    FileName fileName;
-    if(checkError(e, fileName.append(file))) {
-        CORE_LOG_WARNING("cannot append file name '#' with error '#'", file, e);
-        return false;
-    }
-    Result* result = results.search(fileName);
-    if(result == nullptr &&
-       checkError(e, results.tryEmplace(result, fileName))) {
-        CORE_LOG_WARNING("cannot add test result for #, error '#'", file, e);
-        return false;
-    }
-    result->tests++;
     float diff = wanted - actual;
     diff = diff < 0.0f ? -diff : diff;
-    if(diff <= error) {
-        result->successTests++;
-        return true;
-    }
-    CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line, wanted, actual)
-    return false;
+    return check(file, line, wanted, actual, diff <= error);
 }
 
 void Core::Test::finalize() {

+ 18 - 13
test/Test.h

@@ -14,45 +14,51 @@ namespace Core::Test {
         using FileName = String32<256>;
         extern HashMap<FileName, Result> results;
 
+        void warn(const char* file, int line, Error e);
+
         template<typename T>
-        bool checkEqual(const char* file, int line, const T& wanted,
-                        const T& actual) {
+        bool check(const char* file, int line, const T& wanted, const T& actual,
+                   bool c) {
             Error e = Error::NONE;
             FileName fileName;
             if(checkError(e, fileName.append(file))) {
-                CORE_LOG_WARNING("cannot append file name '#' with error '#'",
-                                 file, e);
+                warn(file, line, e);
                 return false;
             }
             Result* result = results.search(fileName);
             if(result == nullptr &&
                checkError(e, results.tryEmplace(result, fileName))) {
-                CORE_LOG_WARNING("cannot add test result for #, error '#'",
-                                 file, e);
+                warn(file, line, e);
                 return false;
             }
             result->tests++;
-            if(wanted == actual) {
+            if(c) {
                 result->successTests++;
                 return true;
             }
             CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line, wanted,
-                           actual)
+                           actual);
             return false;
         }
 
+        template<typename T>
+        bool checkEqual(const char* file, int line, const T& wanted,
+                        const T& actual) {
+            return check(file, line, wanted, actual, wanted == actual);
+        }
+
         template<typename A, typename B>
         bool checkString(const char* file, int line, const A& wanted,
                          const B& actual) {
             Error e = Error::NONE;
             String32<2048> a;
             if(checkError(e, a.append(wanted))) {
-                CORE_LOG_WARNING("error at #:# | ", file, line, e);
+                warn(file, line, e);
                 return false;
             }
             String32<2048> b;
             if(checkError(e, b.append(actual))) {
-                CORE_LOG_WARNING("error at #:# | ", file, line, e);
+                warn(file, line, e);
                 return false;
             }
             return checkEqual(file, line, a, b);
@@ -67,9 +73,8 @@ namespace Core::Test {
                          const Core::Vector<N, T>& actual, float error) {
             bool result = true;
             for(int i = 0; i < N; i++) {
-                result = checkFloat(file, line, static_cast<float>(wanted[i]),
-                                    static_cast<float>(actual[i]), error) &&
-                         result;
+                result &= checkFloat(file, line, static_cast<float>(wanted[i]),
+                                     static_cast<float>(actual[i]), error);
             }
             return result;
         }

+ 705 - 283
tests/ArrayStringTests.cpp

@@ -4,219 +4,198 @@
 #include "test/Test.h"
 #include "utils/ArrayString.h"
 
-template<typename String>
-static String build(const char* cs) {
-    String s;
+using String8 = Core::String8<128>;
+using String32 = Core::String32<128>;
+
+static String8 build(const char* cs) {
+    String8 s;
     CORE_TEST_ERROR(s.append(cs));
     return s;
 }
 
-template<typename String>
-static void testEquality() {
-    String s = build<String>("test");
+static void testEquality8() {
+    String8 s = build("test");
     CORE_TEST_TRUE(s == "test");
-    CORE_TEST_TRUE(s == build<String>("test"));
+    CORE_TEST_TRUE(s == build("test"));
     CORE_TEST_TRUE("test" == s);
-    CORE_TEST_TRUE(build<String>("test") == s);
+    CORE_TEST_TRUE(build("test") == s);
     CORE_TEST_TRUE(s == s);
 }
 
-template<typename String>
-static void testUnicodeEquality() {
+static void testUnicodeEquality8() {
     const char* cs = "\u0040\u0400\u8000\U00100000";
-    String s = build<String>(cs);
+    String8 s = build(cs);
     CORE_TEST_TRUE(s == cs);
-    CORE_TEST_TRUE(s == build<String>(cs));
+    CORE_TEST_TRUE(s == build(cs));
     CORE_TEST_TRUE(cs == s);
-    CORE_TEST_TRUE(build<String>(cs) == s);
+    CORE_TEST_TRUE(build(cs) == s);
     CORE_TEST_TRUE(s == s);
 }
 
-template<typename String>
-static void testInequality() {
-    String s = build<String>("test");
+static void testInequality8() {
+    String8 s = build("test");
     CORE_TEST_FALSE(s != "test");
-    CORE_TEST_FALSE(s != build<String>("test"));
+    CORE_TEST_FALSE(s != build("test"));
     CORE_TEST_FALSE("test" != s);
-    CORE_TEST_FALSE(build<String>("test") != s);
+    CORE_TEST_FALSE(build("test") != s);
     CORE_TEST_FALSE(s != s);
 }
 
-template<typename String>
-static void testStringAppend() {
-    String s = build<String>("test");
+static void testStringAppend8() {
+    String8 s = build("test");
     CORE_TEST_ERROR(s.append("22"));
     CORE_TEST_ERROR(s.append("333"));
     CORE_TEST_ERROR(s.append("4444"));
-    CORE_TEST_EQUAL(build<String>("test223334444"), s);
+    CORE_TEST_EQUAL(build("test223334444"), s);
 }
 
-template<typename String, typename CharType>
-static void testStringAppendOverflow() {
-    Core::ArrayString<6, CharType> s;
+static void testStringAppendOverflow8() {
+    Core::ArrayString<6, char> s;
     CORE_TEST_ERROR(s.append("te"));
     CORE_TEST_EQUAL(Core::Error::CAPACITY_REACHED, s.append("23334444"));
-    CORE_TEST_TRUE(build<String>("te23334444") != s);
+    CORE_TEST_TRUE(build("te23334444") != s);
 }
 
-template<typename String>
-static void testCharacters() {
-    String s = build<String>("test");
+static void testCharacters8() {
+    String8 s = build("test");
     CORE_TEST_EQUAL('t', s[0]);
     CORE_TEST_EQUAL('e', s[1]);
     CORE_TEST_EQUAL('s', s[2]);
     CORE_TEST_EQUAL('t', s[3]);
 }
 
-template<typename String>
-static void testLength() {
-    String s = build<String>("test");
+static void testLength8() {
+    String8 s = build("test");
     CORE_TEST_EQUAL(4, s.getLength());
     CORE_TEST_ERROR(s.append("aaa"));
     CORE_TEST_EQUAL(7, s.getLength());
 }
 
-template<typename String>
-static void testChar() {
-    String s = build<String>("test");
+static void testChar8() {
+    String8 s = build("test");
     for(char i = 'a'; i < 'd'; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("testabc"), s);
+    CORE_TEST_EQUAL(build("testabc"), s);
 }
 
-template<typename String>
-static void testSignedChar() {
-    String s = build<String>("test");
+static void testSignedChar8() {
+    String8 s = build("test");
     for(signed char i = 'b'; i < 'e'; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("testbcd"), s);
+    CORE_TEST_EQUAL(build("testbcd"), s);
 }
 
-template<typename String>
-static void testUnsignedChar() {
-    String s = build<String>("test");
+static void testUnsignedChar8() {
+    String8 s = build("test");
     for(unsigned char i = 'c'; i < 'f'; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("testcde"), s);
+    CORE_TEST_EQUAL(build("testcde"), s);
 }
 
-template<typename String>
-static void testSignedShort() {
-    String s = build<String>("test");
+static void testSignedShort8() {
+    String8 s = build("test");
     for(signed short i = 100; i < 103; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test100101102"), s);
+    CORE_TEST_EQUAL(build("test100101102"), s);
 }
 
-template<typename String>
-static void testUnsignedShort() {
-    String s = build<String>("test");
+static void testUnsignedShort8() {
+    String8 s = build("test");
     for(unsigned short i = 101; i < 104; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test101102103"), s);
+    CORE_TEST_EQUAL(build("test101102103"), s);
 }
 
-template<typename String>
-static void testSignedInt() {
-    String s = build<String>("test");
+static void testSignedInt8() {
+    String8 s = build("test");
     for(signed int i = 102; i < 105; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test102103104"), s);
+    CORE_TEST_EQUAL(build("test102103104"), s);
 }
 
-template<typename String>
-static void testUnsignedInt() {
-    String s = build<String>("test");
+static void testUnsignedInt8() {
+    String8 s = build("test");
     for(unsigned int i = 103; i < 106; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test103104105"), s);
+    CORE_TEST_EQUAL(build("test103104105"), s);
 }
 
-template<typename String>
-static void testSignedLong() {
-    String s = build<String>("test");
+static void testSignedLong8() {
+    String8 s = build("test");
     for(signed long i = 104; i < 107; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test104105106"), s);
+    CORE_TEST_EQUAL(build("test104105106"), s);
 }
 
-template<typename String>
-static void testUnsignedLong() {
-    String s = build<String>("test");
+static void testUnsignedLong8() {
+    String8 s = build("test");
     for(unsigned long i = 105; i < 108; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test105106107"), s);
+    CORE_TEST_EQUAL(build("test105106107"), s);
 }
 
-template<typename String>
-static void testSignedLongLong() {
-    String s = build<String>("test");
+static void testSignedLongLong8() {
+    String8 s = build("test");
     for(signed long long i = 106; i < 109; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test106107108"), s);
+    CORE_TEST_EQUAL(build("test106107108"), s);
 }
 
-template<typename String>
-static void testUnsignedLongLong() {
-    String s = build<String>("test");
+static void testUnsignedLongLong8() {
+    String8 s = build("test");
     for(unsigned long long i = 107; i < 110; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test107108109"), s);
+    CORE_TEST_EQUAL(build("test107108109"), s);
 }
 
-template<typename String>
-static void testFloat() {
-    String s = build<String>("test");
+static void testFloat8() {
+    String8 s = build("test");
     for(float i = 108; i < 111; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test108.00109.00110.00"), s);
+    CORE_TEST_EQUAL(build("test108.00109.00110.00"), s);
 }
 
-template<typename String>
-static void testDouble() {
-    String s = build<String>("test");
+static void testDouble8() {
+    String8 s = build("test");
     for(double i = 109; i < 112; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test109.00110.00111.00"), s);
+    CORE_TEST_EQUAL(build("test109.00110.00111.00"), s);
 }
 
-template<typename String>
-static void testLongDouble() {
-    String s = build<String>("test");
+static void testLongDouble8() {
+    String8 s = build("test");
     for(long double i = 110; i < 113; i++) {
         CORE_TEST_ERROR(s.append(i));
     }
-    CORE_TEST_EQUAL(build<String>("test110.00111.00112.00"), s);
+    CORE_TEST_EQUAL(build("test110.00111.00112.00"), s);
 }
 
-template<typename String>
-static void testBool() {
-    String s = build<String>("test");
+static void testBool8() {
+    String8 s = build("test");
     CORE_TEST_ERROR(s.append(true));
     CORE_TEST_ERROR(s.append(false));
     CORE_TEST_ERROR(s.append(true));
-    CORE_TEST_EQUAL(build<String>("testtruefalsetrue"), s);
+    CORE_TEST_EQUAL(build("testtruefalsetrue"), s);
 }
 
-template<typename String, typename CharType>
-static void testIntOverflow() {
-    Core::ArrayString<4, CharType> s;
+static void testIntOverflow8() {
+    Core::ArrayString<4, char> s;
     CORE_TEST_EQUAL(Core::Error::CAPACITY_REACHED, s.append(123456));
 
-    String o;
+    String8 o;
     for(int i = 0; i < s.getCapacity(); i++) {
         CORE_TEST_ERROR(o.append(i + 1));
     }
@@ -224,59 +203,54 @@ static void testIntOverflow() {
     CORE_TEST_TRUE(o == s);
 }
 
-template<typename String>
-static void testUnicode() {
-    String s;
+static void testUnicode8() {
+    String8 s;
     CORE_TEST_ERROR(s.append('\u0040'));
     CORE_TEST_ERROR(s.append(L'\u0400'));
     CORE_TEST_ERROR(s.append(L'\u8000'));
     CORE_TEST_ERROR(s.append(U'\U00100000'));
-    CORE_TEST_EQUAL(build<String>("\u0040\u0400\u8000\U00100000"), s);
+    CORE_TEST_EQUAL(build("\u0040\u0400\u8000\U00100000"), s);
 }
 
-template<typename String>
-static void testClear() {
-    String s = build<String>("test");
+static void testClear8() {
+    String8 s = build("test");
     CORE_TEST_ERROR(s.append(1234));
     s.clear();
     CORE_TEST_ERROR(s.append("wusi"));
     CORE_TEST_ERROR(s.append("1234"));
-    CORE_TEST_EQUAL(build<String>("wusi1234"), s);
+    CORE_TEST_EQUAL(build("wusi1234"), s);
 }
 
-template<typename String>
-static void testHashCode() {
-    String s;
+static void testHashCode8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("a"));
     CORE_TEST_ERROR(s.append("bc"));
     CORE_TEST_ERROR(s.append(20));
     CORE_TEST_ERROR(s.append(25.5f));
     CORE_TEST_ERROR(s.append(true));
-    CORE_TEST_EQUAL(build<String>("abc2025.50true").hashCode(), s.hashCode());
+    CORE_TEST_EQUAL(build("abc2025.50true").hashCode(), s.hashCode());
     s.clear();
-    CORE_TEST_EQUAL(String().hashCode(), s.hashCode());
+    CORE_TEST_EQUAL(String8().hashCode(), s.hashCode());
 }
 
-template<typename String>
-static void testAddSelf() {
-    String s;
+static void testAddSelf8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("test1"));
     CORE_TEST_ERROR(s.append(s));
     CORE_TEST_ERROR(s.append(s));
-    CORE_TEST_EQUAL(build<String>("test1test1test1test1"), s);
+    CORE_TEST_EQUAL(build("test1test1test1test1"), s);
 }
 
-template<typename String>
-static void testAsHashMapKey() {
-    Core::HashMap<String, int> map;
-    CORE_TEST_ERROR(map.add(build<String>("wusi"), 3));
-    CORE_TEST_ERROR(map.add(build<String>("hiThere"), 7));
-    CORE_TEST_ERROR(map.add(build<String>("baum123"), 5));
+static void testAsHashMapKey8() {
+    Core::HashMap<String8, int> map;
+    CORE_TEST_ERROR(map.add(build("wusi"), 3));
+    CORE_TEST_ERROR(map.add(build("hiThere"), 7));
+    CORE_TEST_ERROR(map.add(build("baum123"), 5));
 
-    int* a = map.search(build<String>("wusi"));
-    int* b = map.search(build<String>("hiThere"));
-    int* c = map.search(build<String>("baum123"));
-    int* d = map.search(build<String>("423hifd"));
+    int* a = map.search(build("wusi"));
+    int* b = map.search(build("hiThere"));
+    int* c = map.search(build("baum123"));
+    int* d = map.search(build("423hifd"));
 
     CORE_TEST_NOT_NULL(a);
     CORE_TEST_NOT_NULL(b);
@@ -290,21 +264,20 @@ static void testAsHashMapKey() {
     }
 }
 
-template<typename String>
-static void testStartsWith() {
-    String s;
+static void testStartsWith8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("0123456789"));
 
-    String s2;
+    String8 s2;
     CORE_TEST_ERROR(s2.append("123"));
-    String s3;
+    String8 s3;
     CORE_TEST_ERROR(s3.append("234"));
-    String s4;
+    String8 s4;
     CORE_TEST_ERROR(s4.append("789"));
-    String s5;
+    String8 s5;
     CORE_TEST_ERROR(s5.append("124"));
-    String s6;
-    String s7;
+    String8 s6;
+    String8 s7;
     CORE_TEST_ERROR(s7.append("7891"));
 
     CORE_TEST_FALSE(s.startsWidth(s2));
@@ -326,21 +299,20 @@ static void testStartsWith() {
     CORE_TEST_FALSE(s.startsWidth(s7, 7));
 }
 
-template<typename String>
-static void testSearch() {
-    String s;
+static void testSearch8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("0123456789"));
 
-    String s2;
+    String8 s2;
     CORE_TEST_ERROR(s2.append("123"));
-    String s3;
+    String8 s3;
     CORE_TEST_ERROR(s3.append("234"));
-    String s4;
+    String8 s4;
     CORE_TEST_ERROR(s4.append("789"));
-    String s5;
+    String8 s5;
     CORE_TEST_ERROR(s5.append("124"));
-    String s6;
-    String s7;
+    String8 s6;
+    String8 s7;
     CORE_TEST_ERROR(s7.append("7891"));
 
     CORE_TEST_EQUAL(1, s.search(s2));
@@ -358,21 +330,20 @@ static void testSearch() {
     CORE_TEST_EQUAL(-1, s.search(s7, 3));
 }
 
-template<typename String>
-static void testContains() {
-    String s;
+static void testContains8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("0123456789"));
 
-    String s2;
+    String8 s2;
     CORE_TEST_ERROR(s2.append("123"));
-    String s3;
+    String8 s3;
     CORE_TEST_ERROR(s3.append("234"));
-    String s4;
+    String8 s4;
     CORE_TEST_ERROR(s4.append("789"));
-    String s5;
+    String8 s5;
     CORE_TEST_ERROR(s5.append("124"));
-    String s6;
-    String s7;
+    String8 s6;
+    String8 s7;
     CORE_TEST_ERROR(s7.append("7891"));
 
     CORE_TEST_TRUE(s.contains(s2));
@@ -383,169 +354,620 @@ static void testContains() {
     CORE_TEST_FALSE(s.contains(s7));
 }
 
-template<typename String, typename CharType>
-static void testSearchChar() {
-    String s;
+static void testSearchChar8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("01üää3ä"));
 
     CORE_TEST_EQUAL(0, s.search('0'));
     CORE_TEST_EQUAL(1, s.search('1'));
-    CORE_TEST_EQUAL((Core::IsSame<CharType, c32> ? 5 : 8), s.search('3'));
-
-    if constexpr(Core::IsSame<CharType, c32>) {
-        CORE_TEST_EQUAL(2, s.search(U'ü'));
-        CORE_TEST_EQUAL(3, s.search(U'ä'));
-        CORE_TEST_EQUAL(4, s.search(U'ä', 4));
-        CORE_TEST_EQUAL(6, s.search(U'ä', 5));
-    }
+    CORE_TEST_EQUAL(8, s.search('3'));
 }
 
-template<typename String, typename CharType>
-static void testContainsChar() {
-    String s;
+static void testContainsChar8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("01üää3ä"));
-
-    if constexpr(Core::IsSame<CharType, c32>) {
-        CORE_TEST_TRUE(s.contains(U'0'));
-        CORE_TEST_TRUE(s.contains(U'1'));
-        CORE_TEST_TRUE(s.contains(U'ü'));
-        CORE_TEST_TRUE(s.contains(U'ä'));
-        CORE_TEST_FALSE(s.contains(U'ö'));
-    } else {
-        CORE_TEST_TRUE(s.contains('0'));
-        CORE_TEST_TRUE(s.contains('1'));
-    }
+    CORE_TEST_TRUE(s.contains('0'));
+    CORE_TEST_TRUE(s.contains('1'));
+    CORE_TEST_TRUE(s.contains('3'));
+    CORE_TEST_FALSE(s.contains('a'));
 }
 
-template<typename String, typename CharType>
-static void testSubString() {
-    String s;
+static void testSubString8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("01üää3ä"));
 
-    if constexpr(Core::IsSame<CharType, c32>) {
-        CORE_TEST_STRING("01üää3ä", s.substring(-2));
-        CORE_TEST_STRING("1üää3ä", s.substring(1));
-        CORE_TEST_STRING("üää3ä", s.substring(2));
-        CORE_TEST_STRING("ää3ä", s.substring(3));
-        CORE_TEST_STRING("ä3ä", s.substring(4));
-
-        CORE_TEST_STRING("01üää3ä", s.substring(0, 6));
-        CORE_TEST_STRING("1üää3", s.substring(1, 5));
-        CORE_TEST_STRING("üää", s.substring(2, 4));
-        CORE_TEST_STRING("ä", s.substring(3, 3));
-        CORE_TEST_STRING("", s.substring(4, 2));
-        CORE_TEST_STRING("ä3ä", s.substring(4, 23));
-    } else {
-        CORE_TEST_STRING("01üää3ä", s.substring(-2));
-        CORE_TEST_STRING("1üää3ä", s.substring(1));
-        CORE_TEST_STRING("üää3ä", s.substring(2));
-        CORE_TEST_STRING("ää3ä", s.substring(4));
-        CORE_TEST_STRING("ä3ä", s.substring(6));
-
-        CORE_TEST_STRING("01üää3ä", s.substring(0, 10));
-        CORE_TEST_STRING("1üää3", s.substring(1, 8));
-        CORE_TEST_STRING("üää", s.substring(2, 7));
-        CORE_TEST_STRING("ä", s.substring(4, 5));
-        CORE_TEST_STRING("", s.substring(4, 2));
-        CORE_TEST_STRING("ä3ä", s.substring(6, 23));
-    }
-}
-
-template<typename String>
-static void testReplace() {
-    String s;
+    CORE_TEST_STRING("01üää3ä", s.substring(-2));
+    CORE_TEST_STRING("1üää3ä", s.substring(1));
+    CORE_TEST_STRING("üää3ä", s.substring(2));
+    CORE_TEST_STRING("ää3ä", s.substring(4));
+    CORE_TEST_STRING("ä3ä", s.substring(6));
+
+    CORE_TEST_STRING("01üää3ä", s.substring(0, 10));
+    CORE_TEST_STRING("1üää3", s.substring(1, 8));
+    CORE_TEST_STRING("üää", s.substring(2, 7));
+    CORE_TEST_STRING("ä", s.substring(4, 5));
+    CORE_TEST_STRING("", s.substring(4, 2));
+    CORE_TEST_STRING("ä3ä", s.substring(6, 23));
+}
+
+static void testReplace8() {
+    String8 s;
     CORE_TEST_ERROR(s.append("0äääää1üää3ä"));
 
-    String search;
+    String8 search;
     CORE_TEST_ERROR(search.append("ää"));
 
-    String replace;
+    String8 replace;
     CORE_TEST_ERROR(replace.append("ABCD"));
 
     CORE_TEST_ERROR(s.replace(search, replace));
 
     CORE_TEST_STRING("0ABCDABCDä1üABCD3ä", s);
 
-    String s2;
+    String8 s2;
     CORE_TEST_ERROR(s2.append("0ABCDABCDä1üABCD3ä"));
     CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
 }
 
-template<typename String, typename CharType>
-static void testReplaceChar() {
-    String s;
-    if constexpr(Core::IsSame<CharType, c32>) {
-        CORE_TEST_ERROR(s.append("01üää3ä"));
-        s.replace(U'0', U'A');
-        CORE_TEST_STRING("A1üää3ä", s);
-        s.replace(U'1', U'B');
-        CORE_TEST_STRING("ABüää3ä", s);
-        s.replace(U'ü', U'C');
-        CORE_TEST_STRING("ABCää3ä", s);
-        s.replace(U'ä', U'D');
-        CORE_TEST_STRING("ABCDD3D", s);
-        s.replace(U'3', U'E');
-        CORE_TEST_STRING("ABCDDED", s);
-    } else {
-        CORE_TEST_ERROR(s.append("01YXX3X"));
-        s.replace('0', 'A');
-        CORE_TEST_STRING("A1YXX3X", s);
-        s.replace('1', 'B');
-        CORE_TEST_STRING("ABYXX3X", s);
-        s.replace('Y', 'C');
-        CORE_TEST_STRING("ABCXX3X", s);
-        s.replace('X', 'D');
-        CORE_TEST_STRING("ABCDD3D", s);
-        s.replace('3', 'E');
-        CORE_TEST_STRING("ABCDDED", s);
-    }
-    String s2;
+static void testReplaceChar8() {
+    String8 s;
+    CORE_TEST_ERROR(s.append("01YXX3X"));
+    s.replace('0', 'A');
+    CORE_TEST_STRING("A1YXX3X", s);
+    s.replace('1', 'B');
+    CORE_TEST_STRING("ABYXX3X", s);
+    s.replace('Y', 'C');
+    CORE_TEST_STRING("ABCXX3X", s);
+    s.replace('X', 'D');
+    CORE_TEST_STRING("ABCDD3D", s);
+    s.replace('3', 'E');
+    CORE_TEST_STRING("ABCDDED", s);
+
+    String8 s2;
     CORE_TEST_ERROR(s2.append("ABCDDED"));
     CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
 }
 
-template<int N, typename CharType>
-static void typedTest() {
-    using String = Core::ArrayString<N, CharType>;
-    testEquality<String>();
-    testUnicodeEquality<String>();
-    testInequality<String>();
-    testStringAppend<String>();
-    testStringAppendOverflow<String, CharType>();
-    testCharacters<String>();
-    testLength<String>();
-    testChar<String>();
-    testSignedChar<String>();
-    testUnsignedChar<String>();
-    testSignedShort<String>();
-    testUnsignedShort<String>();
-    testSignedInt<String>();
-    testUnsignedInt<String>();
-    testSignedLong<String>();
-    testUnsignedLong<String>();
-    testSignedLongLong<String>();
-    testUnsignedLongLong<String>();
-    testFloat<String>();
-    testDouble<String>();
-    testLongDouble<String>();
-    testBool<String>();
-    testIntOverflow<String, CharType>();
-    testUnicode<String>();
-    testClear<String>();
-    testHashCode<String>();
-    testAddSelf<String>();
-    testAsHashMapKey<String>();
-    testStartsWith<String>();
-    testSearch<String>();
-    testContains<String>();
-    testSearchChar<String, CharType>();
-    testContainsChar<String, CharType>();
-    testSubString<String, CharType>();
-    testReplace<String>();
-    testReplaceChar<String, CharType>();
+static void testCastAppendSelf8() {
+    String8 s;
+    CORE_TEST_ERROR(s.append("abc"));
+    CORE_TEST_ERROR(s.append(s));
+    CORE_TEST_ERROR(s.append(static_cast<const char*>(s)));
+    CORE_TEST_STRING("abcabcabcabc", s);
+}
+
+static String32 build(const c32* cs) {
+    String32 s;
+    CORE_TEST_ERROR(s.append(cs));
+    return s;
+}
+
+static void testEquality32() {
+    String32 s = build(U"test");
+    CORE_TEST_TRUE(s == U"test");
+    CORE_TEST_TRUE(s == build(U"test"));
+    CORE_TEST_TRUE(U"test" == s);
+    CORE_TEST_TRUE(build(U"test") == s);
+    CORE_TEST_TRUE(s == s);
+}
+
+static void testUnicodeEquality32() {
+    const c32* cs = U"\u0040\u0400\u8000\U00100000";
+    String32 s = build(cs);
+    CORE_TEST_TRUE(s == cs);
+    CORE_TEST_TRUE(s == build(cs));
+    CORE_TEST_TRUE(cs == s);
+    CORE_TEST_TRUE(build(cs) == s);
+    CORE_TEST_TRUE(s == s);
+}
+
+static void testInequality32() {
+    String32 s = build(U"test");
+    CORE_TEST_FALSE(s != U"test");
+    CORE_TEST_FALSE(s != build(U"test"));
+    CORE_TEST_FALSE(U"test" != s);
+    CORE_TEST_FALSE(build(U"test") != s);
+    CORE_TEST_FALSE(s != s);
+}
+
+static void testStringAppend32() {
+    String32 s = build(U"test");
+    CORE_TEST_ERROR(s.append(U"22"));
+    CORE_TEST_ERROR(s.append(U"333"));
+    CORE_TEST_ERROR(s.append(U"4444"));
+    CORE_TEST_EQUAL(build(U"test223334444"), s);
+}
+
+static void testStringAppendOverflow32() {
+    Core::ArrayString<6, c32> s;
+    CORE_TEST_ERROR(s.append(U"te"));
+    CORE_TEST_EQUAL(Core::Error::CAPACITY_REACHED, s.append(U"23334444"));
+    CORE_TEST_TRUE(build(U"te23334444") != s);
+}
+
+static void testCharacters32() {
+    String32 s = build(U"test");
+    CORE_TEST_EQUAL('t', s[0]);
+    CORE_TEST_EQUAL('e', s[1]);
+    CORE_TEST_EQUAL('s', s[2]);
+    CORE_TEST_EQUAL('t', s[3]);
+}
+
+static void testLength32() {
+    String32 s = build(U"test");
+    CORE_TEST_EQUAL(4, s.getLength());
+    CORE_TEST_ERROR(s.append(U"aaa"));
+    CORE_TEST_EQUAL(7, s.getLength());
+}
+
+static void testChar32() {
+    String32 s = build(U"test");
+    for(char i = 'a'; i < 'd'; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"testabc"), s);
+}
+
+static void testSignedChar32() {
+    String32 s = build(U"test");
+    for(signed char i = 'b'; i < 'e'; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"testbcd"), s);
+}
+
+static void testUnsignedChar32() {
+    String32 s = build(U"test");
+    for(unsigned char i = 'c'; i < 'f'; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"testcde"), s);
+}
+
+static void testSignedShort32() {
+    String32 s = build(U"test");
+    for(signed short i = 100; i < 103; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test100101102"), s);
+}
+
+static void testUnsignedShort32() {
+    String32 s = build(U"test");
+    for(unsigned short i = 101; i < 104; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test101102103"), s);
+}
+
+static void testSignedInt32() {
+    String32 s = build(U"test");
+    for(signed int i = 102; i < 105; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test102103104"), s);
+}
+
+static void testUnsignedInt32() {
+    String32 s = build(U"test");
+    for(unsigned int i = 103; i < 106; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test103104105"), s);
+}
+
+static void testSignedLong32() {
+    String32 s = build(U"test");
+    for(signed long i = 104; i < 107; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test104105106"), s);
+}
+
+static void testUnsignedLong32() {
+    String32 s = build(U"test");
+    for(unsigned long i = 105; i < 108; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test105106107"), s);
+}
+
+static void testSignedLongLong32() {
+    String32 s = build(U"test");
+    for(signed long long i = 106; i < 109; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test106107108"), s);
+}
+
+static void testUnsignedLongLong32() {
+    String32 s = build(U"test");
+    for(unsigned long long i = 107; i < 110; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test107108109"), s);
+}
+
+static void testFloat32() {
+    String32 s = build(U"test");
+    for(float i = 108; i < 111; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test108.00109.00110.00"), s);
+}
+
+static void testDouble32() {
+    String32 s = build(U"test");
+    for(double i = 109; i < 112; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test109.00110.00111.00"), s);
+}
+
+static void testLongDouble32() {
+    String32 s = build(U"test");
+    for(long double i = 110; i < 113; i++) {
+        CORE_TEST_ERROR(s.append(i));
+    }
+    CORE_TEST_EQUAL(build(U"test110.00111.00112.00"), s);
+}
+
+static void testBool32() {
+    String32 s = build(U"test");
+    CORE_TEST_ERROR(s.append(true));
+    CORE_TEST_ERROR(s.append(false));
+    CORE_TEST_ERROR(s.append(true));
+    CORE_TEST_EQUAL(build(U"testtruefalsetrue"), s);
+}
+
+static void testIntOverflow32() {
+    Core::ArrayString<4, c32> s;
+    CORE_TEST_EQUAL(Core::Error::CAPACITY_REACHED, s.append(123456));
+
+    String32 o;
+    for(int i = 0; i < s.getCapacity(); i++) {
+        CORE_TEST_ERROR(o.append(i + 1));
+    }
+
+    CORE_TEST_TRUE(o == s);
+}
+
+static void testUnicode32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append('\u0040'));
+    CORE_TEST_ERROR(s.append(L'\u0400'));
+    CORE_TEST_ERROR(s.append(L'\u8000'));
+    CORE_TEST_ERROR(s.append(U'\U00100000'));
+    CORE_TEST_EQUAL(build(U"\u0040\u0400\u8000\U00100000"), s);
+}
+
+static void testClear32() {
+    String32 s = build(U"test");
+    CORE_TEST_ERROR(s.append(1234));
+    s.clear();
+    CORE_TEST_ERROR(s.append(U"wusi"));
+    CORE_TEST_ERROR(s.append(U"1234"));
+    CORE_TEST_EQUAL(build(U"wusi1234"), s);
+}
+
+static void testHashCode32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"a"));
+    CORE_TEST_ERROR(s.append(U"bc"));
+    CORE_TEST_ERROR(s.append(20));
+    CORE_TEST_ERROR(s.append(25.5f));
+    CORE_TEST_ERROR(s.append(true));
+    CORE_TEST_EQUAL(build(U"abc2025.50true").hashCode(), s.hashCode());
+    s.clear();
+    CORE_TEST_EQUAL(String32().hashCode(), s.hashCode());
+}
+
+static void testAddSelf32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"test1"));
+    CORE_TEST_ERROR(s.append(s));
+    CORE_TEST_ERROR(s.append(s));
+    CORE_TEST_EQUAL(build(U"test1test1test1test1"), s);
+}
+
+static void testAsHashMapKey32() {
+    Core::HashMap<String32, int> map;
+    CORE_TEST_ERROR(map.add(build(U"wusi"), 3));
+    CORE_TEST_ERROR(map.add(build(U"hiThere"), 7));
+    CORE_TEST_ERROR(map.add(build(U"baum123"), 5));
+
+    int* a = map.search(build(U"wusi"));
+    int* b = map.search(build(U"hiThere"));
+    int* c = map.search(build(U"baum123"));
+    int* d = map.search(build(U"423hifd"));
+
+    CORE_TEST_NOT_NULL(a);
+    CORE_TEST_NOT_NULL(b);
+    CORE_TEST_NOT_NULL(c);
+    CORE_TEST_NULL(d);
+
+    if(a != nullptr && b != nullptr && c != nullptr) {
+        CORE_TEST_EQUAL(3, *a);
+        CORE_TEST_EQUAL(7, *b);
+        CORE_TEST_EQUAL(5, *c);
+    }
+}
+
+static void testStartsWith32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"0123456789"));
+
+    String32 s2;
+    CORE_TEST_ERROR(s2.append(U"123"));
+    String32 s3;
+    CORE_TEST_ERROR(s3.append(U"234"));
+    String32 s4;
+    CORE_TEST_ERROR(s4.append(U"789"));
+    String32 s5;
+    CORE_TEST_ERROR(s5.append(U"124"));
+    String32 s6;
+    String32 s7;
+    CORE_TEST_ERROR(s7.append(U"7891"));
+
+    CORE_TEST_FALSE(s.startsWidth(s2));
+    CORE_TEST_TRUE(s.startsWidth(s2, 1));
+
+    CORE_TEST_FALSE(s.startsWidth(s3));
+    CORE_TEST_TRUE(s.startsWidth(s3, 2));
+
+    CORE_TEST_FALSE(s.startsWidth(s4));
+    CORE_TEST_TRUE(s.startsWidth(s4, 7));
+
+    CORE_TEST_FALSE(s.startsWidth(s5));
+    CORE_TEST_FALSE(s.startsWidth(s5, 3));
+
+    CORE_TEST_TRUE(s.startsWidth(s6));
+    CORE_TEST_TRUE(s.startsWidth(s6, 3));
+
+    CORE_TEST_FALSE(s.startsWidth(s7));
+    CORE_TEST_FALSE(s.startsWidth(s7, 7));
+}
+
+static void testSearch32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"0123456789"));
+
+    String32 s2;
+    CORE_TEST_ERROR(s2.append(U"123"));
+    String32 s3;
+    CORE_TEST_ERROR(s3.append(U"234"));
+    String32 s4;
+    CORE_TEST_ERROR(s4.append(U"789"));
+    String32 s5;
+    CORE_TEST_ERROR(s5.append(U"124"));
+    String32 s6;
+    String32 s7;
+    CORE_TEST_ERROR(s7.append(U"7891"));
+
+    CORE_TEST_EQUAL(1, s.search(s2));
+    CORE_TEST_EQUAL(2, s.search(s3));
+    CORE_TEST_EQUAL(7, s.search(s4));
+    CORE_TEST_EQUAL(-1, s.search(s5));
+    CORE_TEST_EQUAL(0, s.search(s6));
+    CORE_TEST_EQUAL(-1, s.search(s7));
+
+    CORE_TEST_EQUAL(-1, s.search(s2, 3));
+    CORE_TEST_EQUAL(-1, s.search(s3, 3));
+    CORE_TEST_EQUAL(7, s.search(s4, 3));
+    CORE_TEST_EQUAL(-1, s.search(s5, 3));
+    CORE_TEST_EQUAL(3, s.search(s6, 3));
+    CORE_TEST_EQUAL(-1, s.search(s7, 3));
+}
+
+static void testContains32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"0123456789"));
+
+    String32 s2;
+    CORE_TEST_ERROR(s2.append(U"123"));
+    String32 s3;
+    CORE_TEST_ERROR(s3.append(U"234"));
+    String32 s4;
+    CORE_TEST_ERROR(s4.append(U"789"));
+    String32 s5;
+    CORE_TEST_ERROR(s5.append(U"124"));
+    String32 s6;
+    String32 s7;
+    CORE_TEST_ERROR(s7.append(U"7891"));
+
+    CORE_TEST_TRUE(s.contains(s2));
+    CORE_TEST_TRUE(s.contains(s3));
+    CORE_TEST_TRUE(s.contains(s4));
+    CORE_TEST_FALSE(s.contains(s5));
+    CORE_TEST_TRUE(s.contains(s6));
+    CORE_TEST_FALSE(s.contains(s7));
+}
+
+static void testSearchChar32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"01üää3ä"));
+
+    CORE_TEST_EQUAL(0, s.search('0'));
+    CORE_TEST_EQUAL(1, s.search('1'));
+    CORE_TEST_EQUAL(5, s.search('3'));
+    CORE_TEST_EQUAL(2, s.search(U'ü'));
+    CORE_TEST_EQUAL(3, s.search(U'ä'));
+    CORE_TEST_EQUAL(4, s.search(U'ä', 4));
+    CORE_TEST_EQUAL(6, s.search(U'ä', 5));
+}
+
+static void testContainsChar32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"01üää3ä"));
+    CORE_TEST_TRUE(s.contains(U'0'));
+    CORE_TEST_TRUE(s.contains(U'1'));
+    CORE_TEST_TRUE(s.contains(U'3'));
+    CORE_TEST_FALSE(s.contains(U'a'));
+    CORE_TEST_TRUE(s.contains(U'0'));
+    CORE_TEST_TRUE(s.contains(U'1'));
+    CORE_TEST_TRUE(s.contains(U'ü'));
+    CORE_TEST_TRUE(s.contains(U'ä'));
+    CORE_TEST_FALSE(s.contains(U'ö'));
+}
+
+static void testSubString32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"01üää3ä"));
+
+    CORE_TEST_STRING(U"01üää3ä", s.substring(-2));
+    CORE_TEST_STRING(U"1üää3ä", s.substring(1));
+    CORE_TEST_STRING(U"üää3ä", s.substring(2));
+    CORE_TEST_STRING(U"ää3ä", s.substring(3));
+    CORE_TEST_STRING(U"ä3ä", s.substring(4));
+
+    CORE_TEST_STRING(U"01üää3ä", s.substring(0, 6));
+    CORE_TEST_STRING(U"1üää3", s.substring(1, 5));
+    CORE_TEST_STRING(U"üää", s.substring(2, 4));
+    CORE_TEST_STRING(U"ä", s.substring(3, 3));
+    CORE_TEST_STRING(U"", s.substring(4, 2));
+    CORE_TEST_STRING(U"ä3ä", s.substring(4, 23));
+}
+
+static void testReplace32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"0äääää1üää3ä"));
+
+    String32 search;
+    CORE_TEST_ERROR(search.append(U"ää"));
+
+    String32 replace;
+    CORE_TEST_ERROR(replace.append(U"ABCD"));
+
+    CORE_TEST_ERROR(s.replace(search, replace));
+
+    CORE_TEST_STRING(U"0ABCDABCDä1üABCD3ä", s);
+
+    String32 s2;
+    CORE_TEST_ERROR(s2.append(U"0ABCDABCDä1üABCD3ä"));
+    CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
+}
+
+static void testReplaceChar32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(U"01üää3ä"));
+    s.replace(U'0', U'A');
+    CORE_TEST_STRING(U"A1üää3ä", s);
+    s.replace(U'1', U'B');
+    CORE_TEST_STRING(U"ABüää3ä", s);
+    s.replace(U'ü', U'C');
+    CORE_TEST_STRING(U"ABCää3ä", s);
+    s.replace(U'ä', U'D');
+    CORE_TEST_STRING(U"ABCDD3D", s);
+    s.replace(U'3', U'E');
+    CORE_TEST_STRING(U"ABCDDED", s);
+
+    String32 s2;
+    CORE_TEST_ERROR(s2.append(U"ABCDDED"));
+    CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
+}
+
+static void testCastAppendSelf32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append("abc"));
+    CORE_TEST_ERROR(s.append(s));
+    CORE_TEST_ERROR(s.append(static_cast<const c32*>(s)));
+    CORE_TEST_STRING("abcabcabcabc", s);
+}
+
+static void testConversion() {
+    const c32* a = U"öüewfde_§$§%$ädsf";
+    const char* b = "öüewfde_§$§%$ädsf";
+
+    String32 sa;
+    CORE_TEST_ERROR(sa.append(a));
+    String8 sb;
+    CORE_TEST_ERROR(sb.append(b));
+
+    String8 sa2;
+    CORE_TEST_ERROR(sa2.append(sa));
+    String32 sb2;
+    CORE_TEST_ERROR(sb2.append(sb));
+
+    CORE_TEST_STRING(a, sa2);
+    CORE_TEST_STRING(b, sb2);
 }
 
 void Core::ArrayStringTests::test() {
-    typedTest<128, c32>();
-    typedTest<128, char>();
+    testEquality8();
+    testUnicodeEquality8();
+    testInequality8();
+    testStringAppend8();
+    testStringAppendOverflow8();
+    testCharacters8();
+    testLength8();
+    testChar8();
+    testSignedChar8();
+    testUnsignedChar8();
+    testSignedShort8();
+    testUnsignedShort8();
+    testSignedInt8();
+    testUnsignedInt8();
+    testSignedLong8();
+    testUnsignedLong8();
+    testSignedLongLong8();
+    testUnsignedLongLong8();
+    testFloat8();
+    testDouble8();
+    testLongDouble8();
+    testBool8();
+    testIntOverflow8();
+    testUnicode8();
+    testClear8();
+    testHashCode8();
+    testAddSelf8();
+    testAsHashMapKey8();
+    testStartsWith8();
+    testSearch8();
+    testContains8();
+    testSearchChar8();
+    testContainsChar8();
+    testSubString8();
+    testReplace8();
+    testReplaceChar8();
+    testCastAppendSelf8();
+
+    testEquality32();
+    testUnicodeEquality32();
+    testInequality32();
+    testStringAppend32();
+    testStringAppendOverflow32();
+    testCharacters32();
+    testLength32();
+    testChar32();
+    testSignedChar32();
+    testUnsignedChar32();
+    testSignedShort32();
+    testUnsignedShort32();
+    testSignedInt32();
+    testUnsignedInt32();
+    testSignedLong32();
+    testUnsignedLong32();
+    testSignedLongLong32();
+    testUnsignedLongLong32();
+    testFloat32();
+    testDouble32();
+    testLongDouble32();
+    testBool32();
+    testIntOverflow32();
+    testUnicode32();
+    testClear32();
+    testHashCode32();
+    testAddSelf32();
+    testAsHashMapKey32();
+    testStartsWith32();
+    testSearch32();
+    testContains32();
+    testSearchChar32();
+    testContainsChar32();
+    testSubString32();
+    testReplace32();
+    testReplaceChar32();
+    testCastAppendSelf32();
+
+    testConversion();
 }

+ 20 - 0
tests/MathTests.cpp

@@ -15,6 +15,25 @@ static void testInterpolate() {
     CORE_TEST_FLOAT(6.0f, CMath::interpolate(0.0f, 10.0f, 0.6f), eps);
 }
 
+static void testIsPowerOf2() {
+    CORE_TEST_TRUE(CMath::isPowerOf2(1));
+    CORE_TEST_TRUE(CMath::isPowerOf2(2));
+    CORE_TEST_FALSE(CMath::isPowerOf2(3));
+    CORE_TEST_TRUE(CMath::isPowerOf2(4));
+    CORE_TEST_FALSE(CMath::isPowerOf2(5));
+    CORE_TEST_FALSE(CMath::isPowerOf2(6));
+    CORE_TEST_FALSE(CMath::isPowerOf2(7));
+    CORE_TEST_TRUE(CMath::isPowerOf2(8));
+    CORE_TEST_FALSE(CMath::isPowerOf2(9));
+    CORE_TEST_FALSE(CMath::isPowerOf2(10));
+    for(int i = 16; i < 30000; i *= 2) {
+        CORE_TEST_TRUE(CMath::isPowerOf2(i));
+        CORE_TEST_FALSE(CMath::isPowerOf2(i + 1));
+        CORE_TEST_FALSE(CMath::isPowerOf2(i + 2));
+        CORE_TEST_FALSE(CMath::isPowerOf2(i + 3));
+    }
+}
+
 static void testRoundUpLog2() {
     CORE_TEST_EQUAL(0, CMath::roundUpLog2(-5));
     CORE_TEST_EQUAL(0, CMath::roundUpLog2(0));
@@ -76,6 +95,7 @@ static void testDegreeToRadian() {
 
 void Core::MathTests::test() {
     testInterpolate();
+    testIsPowerOf2();
     testRoundUpLog2();
     testMin();
     testMax();

+ 24 - 27
utils/ArrayString.h

@@ -18,23 +18,13 @@ namespace Core {
             data[0] = '\0';
         }
 
-        bool operator==(const char* s) const {
-            if constexpr(IsSame<CharType, char>) {
-                for(int i = 0; i < length; i++, s++) {
-                    if(*s == '\0' && *s != data[i]) {
-                        return false;
-                    }
-                }
-                return *s == '\0';
-            } else {
-                for(int i = 0; i < length; i++) {
-                    c32 u = 0;
-                    if(readUnicode(u, s) != Error::NONE || data[i] != u) {
-                        return false;
-                    }
+        bool operator==(const CharType* s) const {
+            for(int i = 0; i < length; i++, s++) {
+                if(*s == '\0' && *s != data[i]) {
+                    return false;
                 }
-                return read(s) == 0;
             }
+            return *s == '\0';
         }
 
         template<int L>
@@ -50,7 +40,7 @@ namespace Core {
             return true;
         }
 
-        bool operator!=(const char* s) const {
+        bool operator!=(const CharType* s) const {
             return !((*this) == s);
         }
 
@@ -99,7 +89,8 @@ namespace Core {
 
         check_return Error append(const char* s) {
             if constexpr(IsSame<CharType, char>) {
-                while(*s != '\0') {
+                // stringLength as s could be some part of data
+                for(int i = stringLength(s); i > 0; i--) {
                     CORE_RETURN_ERROR(append(*(s++)));
                 }
                 return Error::NONE;
@@ -115,6 +106,14 @@ namespace Core {
             }
         }
 
+        check_return Error append(const c32* s) {
+            // stringLength as s could be some part of data
+            for(int i = stringLength(s); i > 0; i--) {
+                CORE_RETURN_ERROR(append(*(s++)));
+            }
+            return Error::NONE;
+        }
+
         check_return Error append(const signed char* s) {
             return append(reinterpret_cast<const char*>(s));
         }
@@ -182,13 +181,13 @@ namespace Core {
 
         template<int L, typename C>
         check_return Error toString(ArrayString<L, C>& s) const {
-            int l = length; // length changes if &s == this
             if constexpr(IsSame<CharType, char> && !IsSame<C, char>) {
+                // utf32 to utf8
                 return s.append(data);
-            } else {
-                for(int i = 0; i < l; i++) {
-                    CORE_RETURN_ERROR(s.append(data[i]));
-                }
+            }
+            int l = length; // length changes if &s == this
+            for(int i = 0; i < l; i++) {
+                CORE_RETURN_ERROR(s.append(data[i]));
             }
             return Error::NONE;
         }
@@ -331,9 +330,7 @@ namespace Core {
             }
         }
 
-        operator const char*() const {
-            static_assert(IsSame<CharType, char>,
-                          "this only works for char strings");
+        operator const CharType*() const {
             return data;
         }
 
@@ -479,12 +476,12 @@ namespace Core {
 }
 
 template<int N, typename CharType>
-bool operator==(const char* cs, const Core::ArrayString<N, CharType>& s) {
+bool operator==(const CharType* cs, const Core::ArrayString<N, CharType>& s) {
     return s == cs;
 }
 
 template<int N, typename CharType>
-bool operator!=(const char* cs, const Core::ArrayString<N, CharType>& s) {
+bool operator!=(const CharType* cs, const Core::ArrayString<N, CharType>& s) {
     return s != cs;
 }
 

+ 11 - 1
utils/Utility.h

@@ -31,7 +31,7 @@ namespace Core {
 #define CORE_RETURN_ERROR(checked)                                             \
     {                                                                          \
         Core::Error error = Core::Error::NONE;                                 \
-        if(checkError(error, checked)) {                                       \
+        if(checkError(error, checked)) [[unlikely]] {                          \
             return error;                                                      \
         }                                                                      \
     }
@@ -174,6 +174,16 @@ namespace Core {
     void free(void* p);
 
     const char* getFileName(const char* path);
+
+    template<typename T>
+    constexpr int stringLength(const T* c) {
+        int i = 0;
+        while(*c != '\0') {
+            c++;
+            i++;
+        }
+        return i;
+    }
 }
 
 using i64 = Core::Internal::SelectSigned<8>;