Browse Source

list to arraylist, list is dynamic, refactoring of several classes,
removal of object pool until it is needed

Kajetan Johannes Hammerle 3 years ago
parent
commit
7e32681799

+ 2 - 4
Main.cpp

@@ -1,6 +1,7 @@
 #include <iostream>
 
 #include "rendering/FileTexture.h"
+#include "tests/ArrayListTests.h"
 #include "tests/ArrayTests.h"
 #include "tests/BitArrayTests.h"
 #include "tests/BufferTests.h"
@@ -8,11 +9,9 @@
 #include "tests/ColorTests.h"
 #include "tests/FrustumTests.h"
 #include "tests/HashMapTests.h"
-#include "tests/HeapArrayTests.h"
 #include "tests/ListTests.h"
 #include "tests/MatrixStackTests.h"
 #include "tests/MatrixTests.h"
-#include "tests/ObjectPoolTests.h"
 #include "tests/PNGReaderTests.h"
 #include "tests/PlaneTests.h"
 #include "tests/QuaternionTests.h"
@@ -34,7 +33,7 @@ int main(int argAmount, char** args) {
         return 0;
     }
     ArrayTests::test();
-    HeapArrayTests::test();
+    ArrayListTests::test();
     HashMapTests::test();
     ListTests::test();
     BitArrayTests::test();
@@ -56,7 +55,6 @@ int main(int argAmount, char** args) {
     BufferTests::test();
     StackAllocatorTests::test();
     TypedBufferTests::test();
-    ObjectPoolTests::test();
     UniquePointerTests::test();
     return 0;
 }

+ 2 - 2
input/Buttons.h

@@ -3,10 +3,10 @@
 
 #include "input/Button.h"
 #include "utils/Array.h"
-#include "utils/List.h"
+#include "utils/ArrayList.h"
 #include "wrapper/Window.h"
 
-typedef List<Button, 32> ButtonList;
+typedef ArrayList<Button, 32> ButtonList;
 
 class Buttons final {
     typedef Array<bool, 32> DownArray;

+ 9 - 9
math/MatrixStack.h

@@ -2,36 +2,36 @@
 #define MATRIXSTACK_H
 
 #include "math/Matrix.h"
-#include "utils/Stack.h"
+#include "utils/ArrayList.h"
 
 template<int N>
 class MatrixStack final {
-    Stack<Matrix, N> stack;
+    ArrayList<Matrix, N> stack;
 
 public:
     MatrixStack() {
-        stack.push(Matrix());
+        stack.add(Matrix());
     }
 
     bool pop() {
-        stack.pop();
-        if(stack.isEmpty()) {
-            stack.push(Matrix());
+        stack.remove(stack.getLength() - 1);
+        if(stack.getLength() == 0) {
+            stack.add(Matrix());
             return true;
         }
         return false;
     }
 
     bool push() {
-        return stack.push(stack.peek());
+        return stack.add(peek());
     }
 
     Matrix& peek() {
-        return stack.peek();
+        return stack[stack.getLength() - 1];
     }
 
     const Matrix& peek() const {
-        return stack.peek();
+        return stack[stack.getLength() - 1];
     }
 
     void clear() {

+ 0 - 59
memory/ObjectPool.h

@@ -1,59 +0,0 @@
-#ifndef OBJECTPOOL_H
-#define OBJECTPOOL_H
-
-#include "utils/UninitializedArray.h"
-
-template<typename T, int N>
-class ObjectPool {
-    int freeIndex;
-
-    union Node {
-        int next;
-        T t;
-
-        template<typename... Args>
-        Node(Args&&... args) : t(std::forward<Args>(args)...) {
-        }
-
-        ~Node() {
-            t.~T();
-        }
-    };
-
-    UninitializedArray<Node, N> data;
-
-public:
-    ObjectPool() : freeIndex(0) {
-        for(int i = 0; i < N - 1; i++) {
-            data[i].next = i + 1;
-        }
-        data[N - 1].next = -1;
-    }
-
-    template<typename... Args>
-    int allocate(Args&&... args) {
-        if(freeIndex == -1) {
-            return -1;
-        }
-        int index = freeIndex;
-        freeIndex = data[index].next;
-        data.init(index, std::forward<Args>(args)...);
-        return index;
-    }
-
-    void free(int p) {
-        data.destroy(p);
-        data[p].next = freeIndex;
-        freeIndex = p;
-    }
-
-    T& operator[](int index) {
-        return data[index].t;
-    }
-
-    const T& operator[](int index) const {
-        return data[index].t;
-    }
-};
-
-#endif

+ 1 - 2
meson.build

@@ -3,7 +3,7 @@ project('gaming core tests', 'cpp')
 sources = ['Main.cpp',
     'tests/Test.cpp',
     'tests/ArrayTests.cpp',
-    'tests/HeapArrayTests.cpp',
+    'tests/ArrayListTests.cpp',
     'tests/HashMapTests.cpp',
     'tests/ListTests.cpp',
     'utils/BitArray.cpp',
@@ -49,7 +49,6 @@ sources = ['Main.cpp',
     'rendering/FileTexture.cpp',
     'memory/StackAllocator.cpp',
     'tests/StackAllocatorTests.cpp',
-    'tests/ObjectPoolTests.cpp',
     'tests/UniquePointerTests.cpp',
     'utils/Buffer.cpp']
 

+ 166 - 0
tests/ArrayListTests.cpp

@@ -0,0 +1,166 @@
+#include "tests/ArrayListTests.h"
+#include "tests/Test.h"
+#include "utils/ArrayList.h"
+#include "utils/StringBuffer.h"
+
+typedef ArrayList<int, 20> IntList;
+typedef StringBuffer<50> String;
+
+static void testAdd(Test& test) {
+    IntList list;
+    list.add(5);
+
+    test.checkEqual(5, list[0], "contains added value");
+    test.checkEqual(1, list.getLength(), "sizes is increased by add");
+}
+
+static void testMultipleAdd(Test& test) {
+    IntList list;
+    list.add(4);
+    list.add(3);
+    list.add(2);
+    test.checkEqual(4, list[0], "contains added value 1");
+    test.checkEqual(3, list[1], "contains added value 2");
+    test.checkEqual(2, list[2], "contains added value 3");
+    test.checkEqual(3, list.getLength(), "sizes is increased by add");
+}
+
+static void testAddReplace(Test& test) {
+    IntList list;
+    list.add(5);
+    list[0] = 3;
+    test.checkEqual(3, list[0], "value is overwritten");
+}
+
+static void testClear(Test& test) {
+    IntList list;
+    list.add(5);
+    list.add(4);
+    list.clear();
+    test.checkEqual(0, list.getLength(), "length is 0 after clear");
+}
+
+static void testOverflow(Test& test) {
+    IntList list;
+    for(int i = 0; i < 20; i++) {
+        test.checkEqual(false, list.add(i),
+                        "add returns false without overflow");
+    }
+    for(int i = 0; i < 1000000; i++) {
+        test.checkEqual(true, list.add(i), "add returns true with overflow");
+    }
+    for(int i = 0; i < list.getLength(); i++) {
+        test.checkEqual(i, list[i], "still contains values after overflow");
+    }
+    test.checkEqual(true, true, "survives overflow");
+}
+
+static void testCopy(Test& test) {
+    IntList list;
+    list.add(1);
+    list.add(2);
+    list.add(3);
+
+    IntList copy(list);
+    test.checkEqual(list.getLength(), copy.getLength(), "copy has same length");
+    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
+        test.checkEqual(list[i], copy[i], "copy has same values");
+    }
+}
+
+static void testCopyAssignment(Test& test) {
+    IntList list;
+    list.add(1);
+    list.add(2);
+    list.add(3);
+
+    IntList copy;
+    copy = list;
+    test.checkEqual(list.getLength(), copy.getLength(),
+                    "copy assignment has same length");
+    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
+        test.checkEqual(list[i], copy[i], "copy assignment has same values");
+    }
+}
+
+static void testMove(Test& test) {
+    IntList list;
+    list.add(1);
+    list.add(2);
+    list.add(3);
+
+    IntList move(std::move(list));
+    test.checkEqual(0, list.getLength(), "moved has length 0");
+    test.checkEqual(3, move.getLength(), "moved passes length");
+    test.checkEqual(1, move[0], "moved passes values");
+    test.checkEqual(2, move[1], "moved passes values");
+    test.checkEqual(3, move[2], "moved passes values");
+}
+
+static void testMoveAssignment(Test& test) {
+    IntList list;
+    list.add(1);
+    list.add(2);
+    list.add(3);
+
+    IntList move;
+    move = std::move(list);
+    test.checkEqual(0, list.getLength(), "assignment moved has length 0");
+    test.checkEqual(3, move.getLength(), "assignment moved passes length");
+    test.checkEqual(1, move[0], "assignment moved passes values");
+    test.checkEqual(2, move[1], "assignment moved passes values");
+    test.checkEqual(3, move[2], "assignment moved passes values");
+}
+
+static void testToString1(Test& test) {
+    IntList list;
+    list.add(1);
+    list.add(243);
+    list.add(-423);
+    test.checkEqual(String("[1, 243, -423]"), String(list), "to string 1");
+}
+
+static void testToString2(Test& test) {
+    IntList list;
+    list.add(1);
+    test.checkEqual(String("[1]"), String(list), "to string 2");
+}
+
+static void testToString3(Test& test) {
+    IntList list;
+    test.checkEqual(String("[]"), String(list), "to string 3");
+}
+
+static void testRemove(Test& test) {
+    IntList list;
+    list.add(4);
+    list.add(3);
+    list.add(2);
+    list.remove(0);
+    test.checkEqual(2, list[0], "remove 1");
+    test.checkEqual(3, list[1], "remove 2");
+    test.checkEqual(2, list.getLength(), "remove 3");
+    list.remove(1);
+    test.checkEqual(2, list[0], "remove 4");
+    test.checkEqual(1, list.getLength(), "remove 5");
+    list.remove(0);
+    test.checkEqual(0, list.getLength(), "remove 6");
+}
+
+void ArrayListTests::test() {
+    Test test("ArrayList");
+    testAdd(test);
+    testMultipleAdd(test);
+    testAddReplace(test);
+    testClear(test);
+    testOverflow(test);
+    testCopy(test);
+    testCopyAssignment(test);
+    testMove(test);
+    testMoveAssignment(test);
+    testToString1(test);
+    testToString2(test);
+    testToString3(test);
+    testRemove(test);
+    test.finalize();
+}

+ 8 - 0
tests/ArrayListTests.h

@@ -0,0 +1,8 @@
+#ifndef ARRAYLISTTESTS_H
+#define ARRAYLISTTESTS_H
+
+namespace ArrayListTests {
+    void test();
+}
+
+#endif

+ 0 - 42
tests/HeapArrayTests.cpp

@@ -1,42 +0,0 @@
-#include "tests/HeapArrayTests.h"
-#include "tests/Test.h"
-#include "utils/HeapArray.h"
-#include "utils/StringBuffer.h"
-
-typedef StringBuffer<50> String;
-
-static void testToString1(Test& test) {
-    HeapArray<int> a(3);
-    a[0] = 1;
-    a[1] = 243;
-    a[2] = -423;
-    test.checkEqual(String("[1, 243, -423]"), String(a), "to string 1");
-}
-
-static void testToString2(Test& test) {
-    HeapArray<int> a(1);
-    a[0] = 1;
-    test.checkEqual(String("[1]"), String(a), "to string 2");
-}
-
-static void testGrow(Test& test) {
-    HeapArray<int> a(3);
-    a[0] = 1;
-    a[1] = 2;
-    a[2] = 3;
-    a.grow(2, 20);
-    test.checkEqual(5, a.getLength(), "resize length");
-    test.checkEqual(1, a[0], "resize copy 1");
-    test.checkEqual(2, a[1], "resize copy 2");
-    test.checkEqual(3, a[2], "resize copy 3");
-    test.checkEqual(20, a[3], "resize copy 4");
-    test.checkEqual(20, a[4], "resize copy 5");
-}
-
-void HeapArrayTests::test() {
-    Test test("HeapArray");
-    testToString1(test);
-    testToString2(test);
-    testGrow(test);
-    test.finalize();
-}

+ 0 - 8
tests/HeapArrayTests.h

@@ -1,8 +0,0 @@
-#ifndef HEAPARRAYTESTS_H
-#define HEAPARRAYTESTS_H
-
-namespace HeapArrayTests {
-    void test();
-}
-
-#endif

+ 16 - 41
tests/ListTests.cpp

@@ -3,7 +3,7 @@
 #include "utils/List.h"
 #include "utils/StringBuffer.h"
 
-typedef List<int, 20> IntList;
+typedef List<int> IntList;
 typedef StringBuffer<50> String;
 
 static void testAdd(Test& test) {
@@ -16,9 +16,7 @@ static void testAdd(Test& test) {
 
 static void testMultipleAdd(Test& test) {
     IntList list;
-    list.add(4);
-    list.add(3);
-    list.add(2);
+    list.add(4).add(3).add(2);
     test.checkEqual(4, list[0], "contains added value 1");
     test.checkEqual(3, list[1], "contains added value 2");
     test.checkEqual(2, list[2], "contains added value 3");
@@ -34,31 +32,25 @@ static void testAddReplace(Test& test) {
 
 static void testClear(Test& test) {
     IntList list;
-    list.add(5);
-    list.add(4);
+    list.add(5).add(4);
     list.clear();
     test.checkEqual(0, list.getLength(), "length is 0 after clear");
 }
 
-static void testOverflow(Test& test) {
+static void testBigAdd(Test& test) {
     IntList list;
-    for(int i = 0; i < 20; i++) {
-        test.checkEqual(false, list.add(i), "add returns false without overflow");
-    }
     for(int i = 0; i < 1000000; i++) {
-        test.checkEqual(true, list.add(i), "add returns true with overflow");
+        list.add(i);
     }
     for(int i = 0; i < list.getLength(); i++) {
-        test.checkEqual(i, list[i], "still contains values after overflow");
+        test.checkEqual(i, list[i], "big add");
     }
-    test.checkEqual(true, true, "survives overflow");
+    test.checkEqual(1000000, list.getLength(), "big add length");
 }
 
 static void testCopy(Test& test) {
     IntList list;
-    list.add(1);
-    list.add(2);
-    list.add(3);
+    list.add(1).add(2).add(3);
 
     IntList copy(list);
     test.checkEqual(list.getLength(), copy.getLength(), "copy has same length");
@@ -69,13 +61,12 @@ static void testCopy(Test& test) {
 
 static void testCopyAssignment(Test& test) {
     IntList list;
-    list.add(1);
-    list.add(2);
-    list.add(3);
+    list.add(1).add(2).add(3);
 
     IntList copy;
     copy = list;
-    test.checkEqual(list.getLength(), copy.getLength(), "copy assignment has same length");
+    test.checkEqual(list.getLength(), copy.getLength(),
+                    "copy assignment has same length");
     for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
         test.checkEqual(list[i], copy[i], "copy assignment has same values");
     }
@@ -83,9 +74,7 @@ static void testCopyAssignment(Test& test) {
 
 static void testMove(Test& test) {
     IntList list;
-    list.add(1);
-    list.add(2);
-    list.add(3);
+    list.add(1).add(2).add(3);
 
     IntList move(std::move(list));
     test.checkEqual(0, list.getLength(), "moved has length 0");
@@ -97,9 +86,7 @@ static void testMove(Test& test) {
 
 static void testMoveAssignment(Test& test) {
     IntList list;
-    list.add(1);
-    list.add(2);
-    list.add(3);
+    list.add(1).add(2).add(3);
 
     IntList move;
     move = std::move(list);
@@ -112,9 +99,7 @@ static void testMoveAssignment(Test& test) {
 
 static void testToString1(Test& test) {
     IntList list;
-    list.add(1);
-    list.add(243);
-    list.add(-423);
+    list.add(1).add(243).add(-423);
     test.checkEqual(String("[1, 243, -423]"), String(list), "to string 1");
 }
 
@@ -131,9 +116,7 @@ static void testToString3(Test& test) {
 
 static void testRemove(Test& test) {
     IntList list;
-    list.add(4);
-    list.add(3);
-    list.add(2);
+    list.add(4).add(3).add(2);
     list.remove(0);
     test.checkEqual(2, list[0], "remove 1");
     test.checkEqual(3, list[1], "remove 2");
@@ -145,20 +128,13 @@ static void testRemove(Test& test) {
     test.checkEqual(0, list.getLength(), "remove 6");
 }
 
-static void testRemoveIsSafe(Test& test) {
-    IntList list;
-    for(int i = -500000; i < 500000; i++) {
-        test.checkEqual(true, list.remove(i), "removing from empty list is safe");
-    }
-}
-
 void ListTests::test() {
     Test test("List");
     testAdd(test);
     testMultipleAdd(test);
     testAddReplace(test);
     testClear(test);
-    testOverflow(test);
+    testBigAdd(test);
     testCopy(test);
     testCopyAssignment(test);
     testMove(test);
@@ -167,6 +143,5 @@ void ListTests::test() {
     testToString2(test);
     testToString3(test);
     testRemove(test);
-    testRemoveIsSafe(test);
     test.finalize();
 }

+ 0 - 75
tests/ObjectPoolTests.cpp

@@ -1,75 +0,0 @@
-#include "tests/ObjectPoolTests.h"
-#include "tests/Test.h"
-#include "memory/ObjectPool.h"
-
-struct A {
-    static int instances;
-    int a;
-
-    A(int a) : a(a) {
-        instances += a;
-    }
-
-    ~A() {
-        instances -= a;
-    }
-};
-
-int A::instances = 0;
-
-static void testAllocateAndFree(Test& test) {
-    ObjectPool<A, 3> pool;
-    int a = pool.allocate(1);
-    int b = pool.allocate(2);
-    int c = pool.allocate(3);
-    int d = pool.allocate(4);
-    int e = pool.allocate(5);
-    test.checkEqual(0, a, "int pointer allocate 1");
-    test.checkEqual(1, b, "int pointer allocate 2");
-    test.checkEqual(2, c, "int pointer allocate 3");
-    test.checkEqual(-1, d, "int pointer allocate 4");
-    test.checkEqual(-1, e, "int pointer allocate 5");
-    pool.free(a);
-    pool.free(b);
-    pool.free(c);
-    test.checkEqual(0, A::instances, "all destructors are called 1");
-}
-
-static void testAllocateAfterFree(Test& test) {
-    ObjectPool<A, 3> pool;
-    int a = pool.allocate(1);
-    int b = pool.allocate(2);
-    int c = pool.allocate(3);
-    
-    pool.free(b);
-    b = pool.allocate(7);
-    test.checkEqual(1, b, "int pointer allocate after free 1");
-    
-    pool.free(c);
-    c = pool.allocate(9);
-    test.checkEqual(2, c, "int pointer allocate after free 2");
-    
-    pool.free(a);
-    a = pool.allocate(11);
-    test.checkEqual(0, a, "int pointer allocate after free 3");
-    
-    pool.free(a);
-    pool.free(b);
-    b = pool.allocate(23);
-    a = pool.allocate(17);
-    test.checkEqual(0, a, "int pointer allocate after free 4");
-    test.checkEqual(1, b, "int pointer allocate after free 5");
-    test.checkEqual(-1, pool.allocate(50), "int pointer allocate after free 6");
-
-    pool.free(a);
-    pool.free(b);
-    pool.free(c);
-    test.checkEqual(0, A::instances, "all destructors are called 2");
-}
-
-void ObjectPoolTests::test() {
-    Test test("ObjectPool");
-    testAllocateAndFree(test);
-    testAllocateAfterFree(test);
-    test.finalize();
-}

+ 0 - 8
tests/ObjectPoolTests.h

@@ -1,8 +0,0 @@
-#ifndef OBJECTPOOLTESTS_H
-#define OBJECTPOOLTESTS_H
-
-namespace ObjectPoolTests {
-    void test();
-}
-
-#endif

+ 14 - 8
tests/SplitStringTests.cpp

@@ -1,18 +1,21 @@
 #include "tests/SplitStringTests.h"
 #include "tests/Test.h"
-#include "utils/SplitString.h"
 #include "utils/List.h"
+#include "utils/SplitString.h"
 
 typedef StringBuffer<60> String;
 typedef SplitString<60> Split;
 
-typedef List<String, 10> StringList;
+typedef List<String> StringList;
 
-static void checkList(Test& test, const char* str, const StringList& list, const char* message) {
+static void checkList(Test& test, const char* str, const StringList& list,
+                      const char* message) {
     Split split(str);
-    test.checkEqual(list.getLength(), split.getLength(), String(message).append(" - split amount"));
+    test.checkEqual(list.getLength(), split.getLength(),
+                    String(message).append(" - split amount"));
     for(int i = 0; i < list.getLength() && i < split.getLength(); i++) {
-        test.checkEqual(list[i], String(split[i]), String(message).append(" ").append(i));
+        test.checkEqual(list[i], String(split[i]),
+                        String(message).append(" ").append(i));
     }
 }
 
@@ -57,7 +60,8 @@ static void testTwoArgumentsWithQuotationMarks(Test& test) {
     StringList list;
     list.add("test");
     list.add("aaa bbbb");
-    checkList(test, "test \"aaa bbbb\"", list, "two arguments and quotation marks");
+    checkList(test, "test \"aaa bbbb\"", list,
+              "two arguments and quotation marks");
 }
 
 static void testThreeArgumentsWithQuotationMarks(Test& test) {
@@ -65,7 +69,8 @@ static void testThreeArgumentsWithQuotationMarks(Test& test) {
     list.add("test");
     list.add("aaa bbbb");
     list.add("ccc");
-    checkList(test, "test \"aaa bbbb\" ccc", list, "three arguments and quotation marks");
+    checkList(test, "test \"aaa bbbb\" ccc", list,
+              "three arguments and quotation marks");
 }
 
 static void testFourArgumentsWithQuotationMarks(Test& test) {
@@ -74,7 +79,8 @@ static void testFourArgumentsWithQuotationMarks(Test& test) {
     list.add("ddd");
     list.add("aaa bbbb");
     list.add("ccc");
-    checkList(test, "test ddd \"aaa bbbb\" ccc", list, "four arguments and quotation marks");
+    checkList(test, "test ddd \"aaa bbbb\" ccc", list,
+              "four arguments and quotation marks");
 }
 
 static void testEmptyArgument(Test& test) {

+ 7 - 9
tests/StackTests.cpp

@@ -8,9 +8,7 @@ typedef StringBuffer<50> String;
 
 static void testPushPopPeek(Test& test) {
     IntStack stack;
-    stack.push(1);
-    stack.push(2);
-    stack.push(3);
+    stack.push(1).push(2).push(3);
     test.checkEqual(3, stack.peek(), "push pop peek 1");
     test.checkEqual(false, stack.pop(), "pop without error 1");
     test.checkEqual(2, stack.peek(), "push pop peek 2");
@@ -20,15 +18,15 @@ static void testPushPopPeek(Test& test) {
     test.checkEqual(true, stack.isEmpty(), "empty after popping all");
 }
 
-static void testOverflow(Test& test) {
+static void testBigPushPop(Test& test) {
     IntStack stack;
-    for(int i = 0; i < 10; i++) {
-        test.checkEqual(false, stack.push(i), "push returns false without overflow");
+    for(int i = 0; i < 1000000; i++) {
+        stack.push(i);
     }
     for(int i = 0; i < 1000000; i++) {
-        test.checkEqual(true, stack.push(i), "push returns true with overflow");
+        test.checkEqual(false, stack.pop(), "big push and pop");
     }
-    test.checkEqual(true, true, "survives overflow");
+    test.checkEqual(true, stack.isEmpty(), "empty after all pops");
 }
 
 static void testToString1(Test& test) {
@@ -60,7 +58,7 @@ static void testPop(Test& test) {
 void StackTests::test() {
     Test test("Stack");
     testPushPopPeek(test);
-    testOverflow(test);
+    testBigPushPop(test);
     testToString1(test);
     testToString2(test);
     testToString3(test);

+ 143 - 0
utils/ArrayList.h

@@ -0,0 +1,143 @@
+#ifndef ARRAYLIST_H
+#define ARRAYLIST_H
+
+#include <new>
+#include <utility>
+
+#include "utils/StringBuffer.h"
+
+template<typename T, int N>
+class ArrayList final {
+    alignas(T) char data[sizeof(T) * N];
+    int length;
+
+public:
+    ArrayList() : length(0) {
+    }
+
+    ArrayList(const ArrayList& other) : ArrayList() {
+        copy(other);
+    }
+
+    ArrayList(ArrayList&& other) : ArrayList() {
+        move(std::move(other));
+        other.clear();
+    }
+
+    ~ArrayList() {
+        clear();
+    }
+
+    ArrayList& operator=(const ArrayList& other) {
+        if(&other != this) {
+            clear();
+            copy(other);
+        }
+        return *this;
+    }
+
+    ArrayList& operator=(ArrayList&& other) {
+        if(&other != this) {
+            clear();
+            move(std::move(other));
+            other.clear();
+        }
+        return *this;
+    }
+
+    T* begin() {
+        return reinterpret_cast<T*>(data);
+    }
+
+    T* end() {
+        return begin() + length;
+    }
+
+    const T* begin() const {
+        return reinterpret_cast<const T*>(data);
+    }
+
+    const T* end() const {
+        return begin() + length;
+    }
+
+    bool add(const T& t) {
+        if(length >= N) {
+            return true;
+        }
+        new(begin() + length++) T(t);
+        return false;
+    }
+
+    bool add(T&& t) {
+        if(length >= N) {
+            return true;
+        }
+        new(begin() + length++) T(std::move(t));
+        return false;
+    }
+
+    template<typename... Args>
+    bool add(Args&&... args) {
+        if(length >= N) {
+            return true;
+        }
+        new(begin() + length++) T(std::forward<Args>(args)...);
+        return false;
+    }
+
+    T& operator[](int index) {
+        return begin()[index];
+    }
+
+    const T& operator[](int index) const {
+        return begin()[index];
+    }
+
+    int getLength() const {
+        return length;
+    }
+
+    void clear() {
+        for(int i = 0; i < length; i++) {
+            begin()[i].~T();
+        }
+        length = 0;
+    }
+
+    void remove(int index) {
+        length--;
+        if(index != length) {
+            begin()[index] = std::move(begin()[length]);
+        }
+        begin()[length].~T();
+    }
+
+    template<int L>
+    void toString(StringBuffer<L>& s) const {
+        s.append("[");
+        for(int i = 0; i < length - 1; i++) {
+            s.append(begin()[i]);
+            s.append(", ");
+        }
+        if(length > 0) {
+            s.append(begin()[length - 1]);
+        }
+        s.append("]");
+    }
+
+private:
+    void copy(const ArrayList& other) {
+        for(int i = 0; i < other.length; i++) {
+            add(other[i]);
+        }
+    }
+
+    void move(ArrayList&& other) {
+        for(int i = 0; i < other.length; i++) {
+            add(std::move(other[i]));
+        }
+    }
+};
+
+#endif

+ 15 - 41
utils/BitArray.cpp

@@ -40,47 +40,27 @@ static void setBits(int* data, int index, int bits, int value) {
     }
 }
 
-BitArray::BitArray(int length, int bits, int value)
-    : length(length), bits(bits), data(new int[roundUpDivide(length * bits, sizeof(int))]) {
-    fill(value);
-}
-
-BitArray::~BitArray() {
-    delete[] data;
+BitArray::BitArray(int length, int bits)
+    : length(length), bits(bits),
+      data(new int[roundUpDivide(length * bits, sizeof(int))]) {
 }
 
 BitArray::BitArray(const BitArray& other) : BitArray(other.length, other.bits) {
-    copyData(other);
+    for(int i = 0; i < length; i++) {
+        set(i, other.get(i));
+    }
 }
 
-BitArray::BitArray(BitArray&& other) : length(other.length), bits(other.bits), data(other.data) {
-    other.reset();
+BitArray::BitArray(BitArray&& other) : length(0), bits(0), data(nullptr) {
+    swap(other);
 }
 
-BitArray& BitArray::operator=(const BitArray& other) {
-    if(this == &other) {
-        return *this;
-    } else if(length == other.length && bits == other.bits) {
-        copyData(other);
-        return *this;
-    }
+BitArray::~BitArray() {
     delete[] data;
-    length = other.length;
-    bits = other.bits;
-    data = new int[roundUpDivide(length * bits, sizeof(int))];
-    copyData(other);
-    return *this;
 }
 
-BitArray& BitArray::operator=(BitArray&& other) {
-    if(this == &other) {
-        return *this;
-    }
-    delete[] data;
-    length = other.length;
-    bits = other.bits;
-    data = other.data;
-    other.reset();
+BitArray& BitArray::operator=(BitArray other) {
+    swap(other);
     return *this;
 }
 
@@ -117,14 +97,8 @@ void BitArray::resize(int newBits) {
     bits = newBits;
 }
 
-void BitArray::copyData(const BitArray& other) {
-    for(int i = 0; i < length; i++) {
-        set(i, other.get(i));
-    }
-}
-
-void BitArray::reset() {
-    length = 0;
-    bits = 0;
-    data = nullptr;
+void BitArray::swap(BitArray& other) {
+    std::swap(length, other.length);
+    std::swap(bits, other.bits);
+    std::swap(data, other.data);
 }

+ 4 - 6
utils/BitArray.h

@@ -9,12 +9,11 @@ class BitArray final {
     int* data;
 
 public:
-    BitArray(int length, int bits, int value = 0);
-    ~BitArray();
+    BitArray(int length, int bits);
     BitArray(const BitArray& other);
     BitArray(BitArray&& other);
-    BitArray& operator=(const BitArray& other);
-    BitArray& operator=(BitArray&& other);
+    ~BitArray();
+    BitArray& operator=(BitArray other);
 
     BitArray& set(int index, int value);
     int get(int index) const;
@@ -40,8 +39,7 @@ public:
     }
 
 private:
-    void copyData(const BitArray& other);
-    void reset();
+    void swap(BitArray& other);
 };
 
 #endif

+ 7 - 4
utils/HashMap.h

@@ -12,8 +12,8 @@ class HashMap final {
     static constexpr int MASK = CAPACITY - 1;
 
     Array<int, CAPACITY> used;
-    List<K, CAPACITY> keys;
-    List<V, CAPACITY> values;
+    List<K> keys;
+    List<V> values;
 
     enum SearchResult { FREE_INDEX_FOUND, KEY_FOUND, NOTHING_FOUND };
 
@@ -51,7 +51,8 @@ public:
     HashMap() : used(-1) {
     }
 
-    HashMap(const HashMap& other) : used(other.used), keys(other.keys), values(other.values) {
+    HashMap(const HashMap& other)
+        : used(other.used), keys(other.keys), values(other.values) {
     }
 
     HashMap& operator=(const HashMap& other) {
@@ -63,7 +64,9 @@ public:
         return *this;
     }
 
-    HashMap(HashMap&& other) : used(other.used), keys(std::move(other.keys)), values(std::move(other.values)) {
+    HashMap(HashMap&& other)
+        : used(other.used), keys(std::move(other.keys)),
+          values(std::move(other.values)) {
         other.used.fill(-1);
     }
 

+ 0 - 104
utils/HeapArray.h

@@ -1,104 +0,0 @@
-#ifndef HEAPARRAY_H
-#define HEAPARRAY_H
-
-#include "utils/StringBuffer.h"
-
-template<typename T>
-class HeapArray final {
-    int length;
-    T* data;
-
-public:
-    HeapArray(int length) : length(length), data(new T[length]) {
-    }
-
-    HeapArray(int length, const T& t) : HeapArray(length) {
-        fill(t);
-    }
-
-    ~HeapArray() {
-        delete[] data;
-    }
-
-    HeapArray(const HeapArray& other) = delete;
-    HeapArray& operator=(const HeapArray& other) = delete;
-
-    HeapArray(HeapArray&& other) : length(other.length), data(other.data) {
-        other.length = 0;
-        other.data = nullptr;
-    }
-
-    HeapArray& operator=(HeapArray&& other) {
-        if(this != &other) {
-            delete[] data;
-            length = other.length;
-            data = other.data;
-            other.length = 0;
-            other.data = nullptr;
-        }
-        return *this;
-    }
-
-    void fill(const T& t) {
-        for(int i = 0; i < length; 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 + length;
-    }
-
-    const T* begin() const {
-        return data;
-    }
-
-    const T* end() const {
-        return data + length;
-    }
-
-    int getLength() const {
-        return length;
-    }
-
-    void grow(int growth, const T& t) {
-        int newLength = length + growth;
-        T* newData = new T[newLength];
-        for(int i = 0; i < length; i++) {
-            newData[i] = std::move(data[i]);
-        }
-        for(int i = length; i < newLength; i++) {
-            newData[i] = t;
-        }
-        delete[] data;
-        data = newData;
-        length = newLength;
-    }
-
-    template<int L>
-    void toString(StringBuffer<L>& s) const {
-        s.append("[");
-        for(int i = 0; i < length - 1; i++) {
-            s.append(data[i]);
-            s.append(", ");
-        }
-        if(length > 0) {
-            s.append(data[length - 1]);
-        }
-        s.append("]");
-    }
-};
-
-#endif

+ 74 - 76
utils/List.h

@@ -1,108 +1,88 @@
 #ifndef LIST_H
 #define LIST_H
 
+#include <new>
+#include <utility>
+
 #include "utils/StringBuffer.h"
-#include "utils/UninitializedArray.h"
 
-template<typename T, int N>
+template<typename T>
 class List final {
-    UninitializedArray<T, N> data;
-    int length = 0;
+    int length;
+    int capacity;
+    T* data;
 
-    void copy(const List& other) {
-        for(int i = 0; i < other.length; i++) {
-            add(other[i]);
-        }
+public:
+    List() : length(0), capacity(0), data(nullptr) {
     }
 
-    void move(List&& other) {
+    List(const List& other)
+        : length(0), capacity(other.capacity), data(allocate(capacity)) {
         for(int i = 0; i < other.length; i++) {
-            add(std::move(other[i]));
+            add(other[i]);
         }
     }
 
-public:
-    List() = default;
-
-    void clear() {
-        for(int i = 0; i < length; i++) {
-            data.destroy(i);
-        }
-        length = 0;
+    List(List&& other) : List() {
+        swap(other);
     }
 
     ~List() {
         clear();
+        delete[] reinterpret_cast<char*>(data);
     }
 
-    List(const List& other) : length(0) {
-        copy(other);
-    }
-
-    List& operator=(const List& other) {
-        if(&other != this) {
-            clear();
-            copy(other);
-        }
-        return *this;
-    }
-
-    List(List&& other) : length(0) {
-        move(std::move(other));
-        other.clear();
-    }
-
-    List& operator=(List&& other) {
-        if(&other != this) {
-            clear();
-            move(std::move(other));
-            other.clear();
-        }
+    List& operator=(List other) {
+        swap(other);
         return *this;
     }
 
     T* begin() {
-        return data.begin();
+        return data;
     }
 
     T* end() {
-        return data.begin() + length;
+        return data + length;
     }
 
     const T* begin() const {
-        return data.begin();
+        return data;
     }
 
     const T* end() const {
-        return data.begin() + length;
+        return data + length;
     }
 
-    bool add(const T& t) {
-        if(length >= N) {
-            return true;
+    void reserve(int n) {
+        if(n <= capacity) {
+            return;
+        }
+        List copy;
+        copy.capacity = n;
+        copy.data = allocate(n);
+        for(int i = 0; i < length; i++) {
+            copy.add(std::move(data[i]));
         }
-        data.init(length, t);
-        length++;
-        return false;
+        swap(copy);
     }
 
-    bool add(T&& t) {
-        if(length >= N) {
-            return true;
-        }
-        data.init(length, std::move(t));
-        length++;
-        return false;
+    List& add(const T& t) {
+        ensureCapacity();
+        new(data + length++) T(t);
+        return *this;
+    }
+
+    List& add(T&& t) {
+        ensureCapacity();
+        new(data + length++) T(std::move(t));
+        return *this;
     }
 
     template<typename... Args>
-    bool add(Args&&... args) {
-        if(length >= N) {
-            return true;
-        }
-        data.init(length, std::forward<Args>(args)...);
-        length++;
-        return false;
+    List& add(Args&&... args) {
+        ensureCapacity();
+        new(data + length++) T(std::forward<Args>(args)...);
+        return *this;
     }
 
     T& operator[](int index) {
@@ -117,6 +97,21 @@ public:
         return length;
     }
 
+    void clear() {
+        for(int i = 0; i < length; i++) {
+            data[i].~T();
+        }
+        length = 0;
+    }
+
+    void remove(int index) {
+        length--;
+        if(index != length) {
+            data[index] = std::move(data[length]);
+        }
+        data[length].~T();
+    }
+
     template<int L>
     void toString(StringBuffer<L>& s) const {
         s.append("[");
@@ -130,18 +125,21 @@ public:
         s.append("]");
     }
 
-    bool remove(int index) {
-        if(index < 0 || index >= length) {
-            return true;
-        } else if(index + 1 == length) {
-            data.destroy(index);
-            length--;
-            return false;
+private:
+    static T* allocate(int n) {
+        return reinterpret_cast<T*>(new char[sizeof(T) * n]);
+    }
+
+    void swap(List& other) {
+        std::swap(length, other.length);
+        std::swap(capacity, other.capacity);
+        std::swap(data, other.data);
+    }
+
+    void ensureCapacity() {
+        if(length >= capacity) {
+            reserve(capacity == 0 ? 8 : capacity * 2);
         }
-        data[index] = std::move(data[length - 1]);
-        data.destroy(length - 1);
-        length--;
-        return false;
     }
 };
 

+ 2 - 2
utils/SplitString.h

@@ -6,8 +6,8 @@
 
 template<int N>
 class SplitString final {
-    List<int, N> entries;
-    List<char, N> data;
+    List<int> entries;
+    List<char> data;
 
     bool fill(const StringBuffer<N>& s) {
         for(int i = 0; i < s.getLength(); i++) {

+ 15 - 8
utils/Stack.h

@@ -5,20 +5,23 @@
 
 template<typename T, int N>
 class Stack final {
-    List<T, N> data;
+    List<T> data;
 
 public:
-    bool push(const T& t) {
-        return data.add(t);
+    Stack& push(const T& t) {
+        data.add(t);
+        return *this;
     }
 
-    bool push(T&& t) {
-        return data.add(std::move(t));
+    Stack& push(T&& t) {
+        data.add(std::move(t));
+        return *this;
     }
 
     template<typename... Args>
-    bool push(Args&&... args) {
-        return data.add(std::forward<Args>(args)...);
+    Stack& push(Args&&... args) {
+        data.add(std::forward<Args>(args)...);
+        return *this;
     }
 
     void clear() {
@@ -26,7 +29,11 @@ public:
     }
 
     bool pop() {
-        return data.remove(data.getLength() - 1);
+        if(data.getLength() <= 0) {
+            return true;
+        }
+        data.remove(data.getLength() - 1);
+        return false;
     }
 
     bool isEmpty() const {

+ 0 - 60
utils/UninitializedArray.h

@@ -1,60 +0,0 @@
-#ifndef UNINITIALIZEDARRAY_H
-#define UNINITIALIZEDARRAY_H
-
-#include <new>
-#include <utility>
-
-template<typename T, int N>
-class UninitializedArray final {
-    char data[sizeof(T) * N];
-
-public:
-    UninitializedArray() = default;
-    UninitializedArray(const UninitializedArray&) = delete;
-    UninitializedArray(UninitializedArray&&) = delete;
-    UninitializedArray& operator=(const UninitializedArray&) = delete;
-    UninitializedArray& operator=(UninitializedArray&&) = delete;
-
-    T* begin() {
-        return reinterpret_cast<T*>(data);
-    }
-
-    T* end() {
-        return begin() + N;
-    }
-
-    const T* begin() const {
-        return reinterpret_cast<const T*>(data);
-    }
-
-    const T* end() const {
-        return begin() + N;
-    }
-
-    T& operator[](int index) {
-        return begin()[index];
-    }
-
-    const T& operator[](int index) const {
-        return begin()[index];
-    }
-
-    void init(int index, const T& t) {
-        new(begin() + index) T(t);
-    }
-
-    void init(int index, T&& t) {
-        new(begin() + index) T(std::move(t));
-    }
-
-    template<typename... Args>
-    void init(int index, Args&&... args) {
-        new(begin() + index) T(std::forward<Args>(args)...);
-    }
-
-    void destroy(int index) {
-        begin()[index].~T();
-    }
-};
-
-#endif

+ 2 - 2
wrapper/Attributes.h

@@ -1,7 +1,7 @@
 #ifndef ATTRIBUTES_H
 #define ATTRIBUTES_H
 
-#include "utils/List.h"
+#include "utils/ArrayList.h"
 
 class Attributes final {
     struct Attribute final {
@@ -11,7 +11,7 @@ class Attributes final {
         bool normalized;
     };
 
-    List<Attribute, 10> attributes;
+    ArrayList<Attribute, 10> attributes;
 
     friend class VertexBuffer;
 

+ 20 - 10
wrapper/Framebuffer.h

@@ -3,13 +3,13 @@
 
 #include <iostream>
 
-#include "utils/List.h"
+#include "utils/ArrayList.h"
 #include "utils/Size.h"
 #include "wrapper/Texture.h"
 
 template<int N>
 class Framebuffer final {
-    List<Texture, N> textures;
+    ArrayList<Texture, N> textures;
     GLuint buffer;
 
 public:
@@ -17,7 +17,8 @@ public:
     Framebuffer(const TextureFormat& a, Args&&... args) : buffer(0) {
         const int size = sizeof...(args) + 1;
         TextureFormat init[size] = {a, args...};
-        static_assert(N == size, "framebuffer size and amount of arguments do not match");
+        static_assert(N == size,
+                      "framebuffer size and amount of arguments do not match");
         for(int i = 0; i < N; i++) {
             textures.add(init[i]);
             textures[i].setClampWrap();
@@ -40,14 +41,16 @@ public:
         glGenFramebuffers(1, &buffer);
         glBindFramebuffer(GL_FRAMEBUFFER, buffer);
 
-        List<GLenum, N> attachments;
+        ArrayList<GLenum, N> attachments;
         for(Texture& t : textures) {
             t.setData(size.width, size.height);
             if(t.format.depth) {
-                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, t.texture, 0);
+                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                                       GL_TEXTURE_2D, t.texture, 0);
             } else {
                 GLenum c = GL_COLOR_ATTACHMENT0 + attachments.getLength();
-                glFramebufferTexture2D(GL_FRAMEBUFFER, c, GL_TEXTURE_2D, t.texture, 0);
+                glFramebufferTexture2D(GL_FRAMEBUFFER, c, GL_TEXTURE_2D,
+                                       t.texture, 0);
                 attachments.add(c);
             }
         }
@@ -58,7 +61,8 @@ public:
 
     void bindAndClear() {
         glBindFramebuffer(GL_FRAMEBUFFER, buffer);
-        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
+                GL_STENCIL_BUFFER_BIT);
     }
 
     void bindTextureTo(int index, int textureUnit) const {
@@ -78,8 +82,12 @@ private:
             return false;
         }
         switch(error) {
-            case GL_FRAMEBUFFER_UNDEFINED: std::cout << "undefined framebuffer\n"; return true;
-            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: std::cout << "incomplete framebuffer attachment\n"; return true;
+            case GL_FRAMEBUFFER_UNDEFINED:
+                std::cout << "undefined framebuffer\n";
+                return true;
+            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+                std::cout << "incomplete framebuffer attachment\n";
+                return true;
             case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
                 std::cout << "incomplete missing framebuffer attachment\n";
                 return true;
@@ -89,7 +97,9 @@ private:
             case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
                 std::cout << "incomplete framebuffer read buffer\n";
                 return true;
-            case GL_FRAMEBUFFER_UNSUPPORTED: std::cout << "unsupported framebuffer\n"; return true;
+            case GL_FRAMEBUFFER_UNSUPPORTED:
+                std::cout << "unsupported framebuffer\n";
+                return true;
             case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
                 std::cout << "incomplete framebuffer multisample\n";
                 return true;