updated structure of allocators in core libraries
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 18 Dec 2020 03:27:58 +0000 (21:27 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 18 Dec 2020 03:27:58 +0000 (21:27 -0600)
12 files changed:
core/alloc.onyx
core/alloc/arena.onyx [new file with mode: 0644]
core/alloc/fixed.onyx [new file with mode: 0644]
core/alloc/heap.onyx [new file with mode: 0644]
core/alloc/ring.onyx [new file with mode: 0644]
core/builtin.onyx
core/file.onyx
core/sys/js.onyx
core/sys/wasi.onyx
onyx
src/onyxsymres.c
tests/general1

index 53321a3475dfcfa98b3c7678d0339b9ec5649bf8..99d3f86cd5e7ec74cda45e37e3a089417e538492 100644 (file)
-package core.allocator
+package core.alloc
 
-#include_file "core/memory"
-#include_file "core/intrinsics/wasm"
+#include_file "core/alloc/arena"
+#include_file "core/alloc/fixed"
+#include_file "core/alloc/heap"
+#include_file "core/alloc/ring"
 
-use package core.intrinsics.wasm { memory_size, memory_grow }
-use package core.memory as memory
+TEMPORARY_ALLOCATOR_SIZE :: 1 << 12; // 16Kb
 
+// The global heap allocator, set up upon program intialization.
 heap_allocator : Allocator;
 
-#private
-heap_state : struct {
-    free_list       : ^heap_block;
-    next_alloc      : rawptr;
-    remaining_space : u32;
-}
-
-#private
-heap_block :: struct {
-    size : u32;
-    next : ^heap_block;
-}
-
-heap_init :: proc () {
-    heap_state.free_list = null;
-    heap_state.next_alloc = __heap_start;
-    heap_state.remaining_space = (memory_size() << 16) - cast(u32) __heap_start;
-
-    heap_allocator.data = ^heap_state;
-    heap_allocator.func = heap_alloc_proc;
-}
-
-#private
-heap_alloc :: proc (size_: u32, align: u32) -> rawptr {
-    if size_ == 0 do return null;
-
-    size := size_ + sizeof heap_block;
-    if size % align != 0 {
-        size += align - (size % align);
-    }
-
-    prev := ^heap_state.free_list;
-    hb := heap_state.free_list;
-    while hb != null {
-        if hb.size >= size {
-            *prev = hb.next;
-            hb.next = null;
-
-            return cast(rawptr) (cast(u32) hb + sizeof heap_block);
-        }
-
-        prev = ^hb.next;
-        hb = hb.next;
-    }
-
-    if size < heap_state.remaining_space {
-        ret := cast(^heap_block) heap_state.next_alloc;
-        ret.size = size;
-        ret.next = null;
-
-        heap_state.next_alloc = cast(rawptr) (cast(u32) heap_state.next_alloc + size);
-        heap_state.remaining_space -= size;
-
-        return cast(rawptr) (cast(u32) ret + sizeof heap_block);
-    }
-
-    new_pages :: ((size - heap_state.remaining_space) >> 16) + 1;
-    if memory_grow(new_pages) == -1 {
-        // out of memory
-        return null;
-    }
-    heap_state.remaining_space += new_pages << 16;
-
-    ret := cast(^heap_block) heap_state.next_alloc;
-    ret.size = size;
-    ret.next = null;
-
-    heap_state.next_alloc = cast(rawptr) (cast(u32) heap_state.next_alloc + size);
-    heap_state.remaining_space -= size;
-
-    return cast(rawptr) (cast(u32) ret + sizeof heap_block);
-}
-
-#private
-heap_free :: proc (ptr: rawptr) {
-    hb_ptr := cast(^heap_block) (cast(u32) ptr - sizeof heap_block);
-
-    // DEBUGGING: Fills freed memory with 0's
-    // for i: 0, hb_ptr.size do (cast(^u8) ptr)[i] = cast(u8) 0;
-
-    hb_ptr.next = heap_state.free_list;
-    heap_state.free_list = hb_ptr;
-}
-
-#private
-heap_resize :: proc (ptr: rawptr, new_size: u32, align: u32) -> rawptr {
-    hb_ptr := cast(^heap_block) (cast(u32) ptr - sizeof heap_block);
-    old_size := hb_ptr.size - sizeof heap_block;
-
-    // If there is already enough space in the current allocated block,
-    // just return the block that already exists and has the memory in it.
-    if old_size >= new_size do return ptr;
-
-    // If we are at the end of the allocation space, just extend it
-    if hb_ptr.size + cast(u32) ptr >= cast(u32) heap_state.next_alloc {
-        if new_size - old_size >= heap_state.remaining_space {
-            new_pages :: ((new_size - old_size - heap_state.remaining_space) >> 16) + 1;
-            if memory_grow(new_pages) == -1 {
-                // out of memory
-                return null;
-            }
-            heap_state.remaining_space += new_pages << 16;
-        }
-
-        hb_ptr.size = new_size + sizeof heap_block;
-        heap_state.next_alloc = cast(rawptr) (cast(u32) ptr + hb_ptr.size);
-        heap_state.remaining_space -= new_size - old_size;
-        return ptr;
-    }
-
-    new_ptr := heap_alloc(new_size, align);
-    memory.copy(new_ptr, ptr, old_size);
-    heap_free(ptr);
-    return new_ptr;
-}
-
-#private
-heap_alloc_proc :: proc (data: rawptr, aa: AllocAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
-    if aa == AllocAction.Alloc do return heap_alloc(size, align);
-    if aa == AllocAction.Resize do return heap_resize(oldptr, size, align);
-    if aa == AllocAction.Free {
-        heap_free(oldptr);
-        return null;
-    }
-
-    return null;
-}
-
-
-ScratchState :: struct {
-    base_ptr : rawptr;
-    size     : u32;
-    curr_ptr : rawptr;
-}
-
-#private
-scratch_alloc_proc :: proc (data: rawptr, aa: AllocAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
-    ss := cast(^ScratchState) data;
-
-    if aa == AllocAction.Alloc {
-        retval := null;
-        rem := ss.size - cast(u32) ss.curr_ptr + cast(u32) ss.base_ptr;
-
-        if size <= rem {
-            retval = ss.curr_ptr;
-            ss.curr_ptr = cast(rawptr) (cast(u32) ss.curr_ptr + size);
-        } else {
-            ss.curr_ptr = ss.base_ptr;
-            retval = ss.base_ptr;
-        }
-
-        return retval;
-    }
-
-    return null;
-}
-
-scratch_state_init :: proc (use ss: ^ScratchState, buffer: rawptr, length: u32) {
-    base_ptr = buffer;
-    curr_ptr = buffer;
-    size     = length;
-}
-
-scratch_alloc_init :: proc (a: ^Allocator, ss: ^ScratchState) {
-    a.func = scratch_alloc_proc;
-    a.data = ss;
-}
-
+// The global temp allocator, set up upon program intialization.
+#private_file
+temp_state     : ring.RingState;
+temp_allocator : Allocator;
 
 
 alloc_slice :: proc (sl: ^[] $T, count: i32) {
@@ -183,4 +21,10 @@ alloc_slice :: proc (sl: ^[] $T, count: i32) {
     sl.count = count;
 }
 
+init :: proc () {
+    heap.init();
 
+    temp_buffer := raw_alloc(heap_allocator, TEMPORARY_ALLOCATOR_SIZE);
+    temp_state = ring.make(temp_buffer, TEMPORARY_ALLOCATOR_SIZE);
+    temp_allocator = ring.make_allocator(^temp_state);
+}
diff --git a/core/alloc/arena.onyx b/core/alloc/arena.onyx
new file mode 100644 (file)
index 0000000..9418019
--- /dev/null
@@ -0,0 +1,3 @@
+package core.alloc.arena
+
+// TODO: Implement the arena allocator
\ No newline at end of file
diff --git a/core/alloc/fixed.onyx b/core/alloc/fixed.onyx
new file mode 100644 (file)
index 0000000..9a46ea1
--- /dev/null
@@ -0,0 +1,40 @@
+package core.alloc.fixed
+
+// This allocator is very simple and always returns the same pointer,
+// unless too much memory is asked for, in which case it returns null.
+//
+// This kind of allocator is useful for temporary string building or
+// similar circumstances, where you know that the needed memory size
+// will not be exceeded, but you don't what to deal with potential
+// slowness of a general heap allocator. By using this allocator, you
+// can continue to use the same code that does allocations like normal,
+// but can get the speed increase of a simple allocation strategy.
+
+FixedAllocatorData :: struct {
+       ptr  : rawptr;
+       size : u32;
+}
+
+#private_file
+fixed_allocator_proc :: proc (data: rawptr, aa: AllocationAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
+       fa_data := cast(^FixedAllocatorData) data;      
+
+       if aa != AllocationAction.Alloc do return null;
+       if size > fa_data.size do return null;
+       
+       return fa_data.ptr;
+}
+
+make :: proc (ptr: rawptr, size: u32) -> FixedAllocatorData {
+       return FixedAllocatorData.{
+               ptr  = ptr,
+               size = size,
+       };
+}
+
+make_allocator :: proc (fa_data: ^FixedAllocatorData) -> Allocator {
+       return Allocator.{
+               func = fixed_allocator_proc,
+               data = fa_data,
+       };
+}
diff --git a/core/alloc/heap.onyx b/core/alloc/heap.onyx
new file mode 100644 (file)
index 0000000..24add47
--- /dev/null
@@ -0,0 +1,143 @@
+package core.alloc.heap
+
+// This is the implementation for the general purpose heap allocator.
+// It is a simple bump allocator, with a free list. It is not very good
+// but it suffices for the kinds of things being done in the early days
+// of the language. You will not make your own instance of the heap
+// allocator, since it controls WASM intrinsics such as memory_grow.
+
+#include_file "core/memory"
+#include_file "core/intrinsics/wasm"
+
+use package core.intrinsics.wasm { memory_size, memory_grow }
+use package core.memory as memory
+
+// The global heap state
+#private_file
+heap_state : struct {
+    free_list       : ^heap_block;
+    next_alloc      : rawptr;
+    remaining_space : u32;
+}
+
+#private_file
+heap_block :: struct {
+    size : u32;
+    next : ^heap_block;
+}
+
+#private_file
+heap_alloc :: proc (size_: u32, align: u32) -> rawptr {
+    if size_ == 0 do return null;
+
+    size := size_ + sizeof heap_block;
+    if size % align != 0 {
+        size += align - (size % align);
+    }
+
+    prev := ^heap_state.free_list;
+    hb := heap_state.free_list;
+    while hb != null {
+        if hb.size >= size {
+            *prev = hb.next;
+            hb.next = null;
+
+            return cast(rawptr) (cast(u32) hb + sizeof heap_block);
+        }
+
+        prev = ^hb.next;
+        hb = hb.next;
+    }
+
+    if size < heap_state.remaining_space {
+        ret := cast(^heap_block) heap_state.next_alloc;
+        ret.size = size;
+        ret.next = null;
+
+        heap_state.next_alloc = cast(rawptr) (cast(u32) heap_state.next_alloc + size);
+        heap_state.remaining_space -= size;
+
+        return cast(rawptr) (cast(u32) ret + sizeof heap_block);
+    }
+
+    new_pages :: ((size - heap_state.remaining_space) >> 16) + 1;
+    if memory_grow(new_pages) == -1 {
+        // out of memory
+        return null;
+    }
+    heap_state.remaining_space += new_pages << 16;
+
+    ret := cast(^heap_block) heap_state.next_alloc;
+    ret.size = size;
+    ret.next = null;
+
+    heap_state.next_alloc = cast(rawptr) (cast(u32) heap_state.next_alloc + size);
+    heap_state.remaining_space -= size;
+
+    return cast(rawptr) (cast(u32) ret + sizeof heap_block);
+}
+
+#private_file
+heap_free :: proc (ptr: rawptr) {
+    hb_ptr := cast(^heap_block) (cast(u32) ptr - sizeof heap_block);
+
+    // DEBUGGING: Fills freed memory with 0's
+    // for i: 0, hb_ptr.size do (cast(^u8) ptr)[i] = cast(u8) 0;
+
+    hb_ptr.next = heap_state.free_list;
+    heap_state.free_list = hb_ptr;
+}
+
+#private_file
+heap_resize :: proc (ptr: rawptr, new_size: u32, align: u32) -> rawptr {
+    hb_ptr := cast(^heap_block) (cast(u32) ptr - sizeof heap_block);
+    old_size := hb_ptr.size - sizeof heap_block;
+
+    // If there is already enough space in the current allocated block,
+    // just return the block that already exists and has the memory in it.
+    if old_size >= new_size do return ptr;
+
+    // If we are at the end of the allocation space, just extend it
+    if hb_ptr.size + cast(u32) ptr >= cast(u32) heap_state.next_alloc {
+        if new_size - old_size >= heap_state.remaining_space {
+            new_pages :: ((new_size - old_size - heap_state.remaining_space) >> 16) + 1;
+            if memory_grow(new_pages) == -1 {
+                // out of memory
+                return null;
+            }
+            heap_state.remaining_space += new_pages << 16;
+        }
+
+        hb_ptr.size = new_size + sizeof heap_block;
+        heap_state.next_alloc = cast(rawptr) (cast(u32) ptr + hb_ptr.size);
+        heap_state.remaining_space -= new_size - old_size;
+        return ptr;
+    }
+
+    new_ptr := heap_alloc(new_size, align);
+    memory.copy(new_ptr, ptr, old_size);
+    heap_free(ptr);
+    return new_ptr;
+}
+
+#private_file
+heap_alloc_proc :: proc (data: rawptr, aa: AllocationAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
+    if aa == AllocationAction.Alloc do return heap_alloc(size, align);
+    if aa == AllocationAction.Resize do return heap_resize(oldptr, size, align);
+    if aa == AllocationAction.Free {
+        heap_free(oldptr);
+        return null;
+    }
+
+    return null;
+}
+
+init :: proc () {
+    heap_state.free_list = null;
+    heap_state.next_alloc = __heap_start;
+    heap_state.remaining_space = (memory_size() << 16) - cast(u32) __heap_start;
+
+    use package core.alloc { heap_allocator }
+    heap_allocator.data = ^heap_state;
+    heap_allocator.func = heap_alloc_proc;
+}
\ No newline at end of file
diff --git a/core/alloc/ring.onyx b/core/alloc/ring.onyx
new file mode 100644 (file)
index 0000000..d93fd9b
--- /dev/null
@@ -0,0 +1,55 @@
+package core.alloc.ring
+
+// This allocator is great for temporary memory, such as returning
+// a pointer from a function, or storing a formatted string. The
+// memory allocated using this allocator does not need to be freed.
+// The idea is that as you keep allocating you will "wrap around"
+// and start writing over memory that was allocated before. For this
+// reason, it is not safe to use this for any kind of permanent
+// allocation. Also, be wary that you provide this allocator with
+// a buffer big enough to store as much data as you are going to need
+// at any given time. 
+
+RingState :: struct {
+    base_ptr : rawptr;
+    size     : u32;
+    curr_ptr : rawptr;
+}
+
+#private_file
+ring_alloc_proc :: proc (data: rawptr, aa: AllocationAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
+    ss := cast(^RingState) data;
+
+    if aa == AllocationAction.Alloc {
+        retval := null;
+        rem := ss.size - cast(u32) ss.curr_ptr + cast(u32) ss.base_ptr;
+
+        if size <= rem {
+            retval = ss.curr_ptr;
+            ss.curr_ptr = cast(rawptr) (cast(u32) ss.curr_ptr + size);
+        } else {
+            ss.curr_ptr = ss.base_ptr;
+            retval = ss.base_ptr;
+        }
+
+        return retval;
+    }
+
+    return null;
+}
+
+make :: proc (buffer: rawptr, length: u32) -> RingState {
+       return RingState.{
+           base_ptr = buffer,
+           curr_ptr = buffer,
+           size     = length,
+       };
+}
+
+make_allocator :: proc (rs: ^RingState) -> Allocator {
+       return Allocator.{
+               func = ring_alloc_proc,
+               data = rs,
+       };
+}
+
index de79b9734005ebbc03dfd3ea588939b0c9f17f90..0a7df2d1dab0728787eb810e228ba3f3b587d636 100644 (file)
@@ -26,45 +26,53 @@ vararg_get :: proc (va: vararg, ret: ^$T) -> bool {
 
 null :: cast(rawptr) 0;
 
-// ---------------------------------
-//           Allocation
-// ---------------------------------
-DEFAULT_ALLOCATION_ALIGNMENT :: 16
+OnyxContext :: struct {
+    allocator      : Allocator;
+    temp_allocator : Allocator;
+}
+
+context : OnyxContext;
+
 
-AllocAction :: enum {
+//
+// Basic allocation structures.
+// The implementations of all of the allocators can be found in core/alloc/.
+// These need to be here so the context structure has the types and enum values.
+//
+__DEFAULT_ALLOCATION_ALIGNMENT :: 16
+
+AllocationAction :: enum {
     Alloc;
     Free;
     Resize;
 }
 
-allocator_proc :: #type proc (rawptr, AllocAction, u32, u32, rawptr) -> rawptr;
+allocator_proc :: #type proc (rawptr, AllocationAction, u32, u32, rawptr) -> rawptr;
 
 Allocator :: struct {
     data: rawptr;
     func: allocator_proc;
 }
 
-alloc :: proc (use a: Allocator, size: u32) -> rawptr {
-    return func(data, AllocAction.Alloc, size, DEFAULT_ALLOCATION_ALIGNMENT, null);
+raw_alloc :: proc (use a: Allocator, size: u32) -> rawptr {
+    return func(data, AllocationAction.Alloc, size, __DEFAULT_ALLOCATION_ALIGNMENT, null);
 }
 
-resize :: proc (use a: Allocator, ptr: rawptr, size: u32) -> rawptr {
-    return func(data, AllocAction.Resize, size, DEFAULT_ALLOCATION_ALIGNMENT, ptr);
+raw_resize :: proc (use a: Allocator, ptr: rawptr, size: u32) -> rawptr {
+    return func(data, AllocationAction.Resize, size, __DEFAULT_ALLOCATION_ALIGNMENT, ptr);
 }
 
-free :: proc (use a: Allocator, ptr: rawptr) {
-    func(data, AllocAction.Free, 0, 0, ptr);
+raw_free :: proc (use a: Allocator, ptr: rawptr) {
+    func(data, AllocationAction.Free, 0, 0, ptr);
 }
 
-calloc  :: proc (size: u32) -> rawptr do return alloc(context.allocator, size);
-cresize :: proc (ptr: rawptr, size: u32) -> rawptr do return resize(context.allocator, ptr, size);
-cfree   :: proc (ptr: rawptr) do free(context.allocator, ptr);
+// Allocators using the context structure.
+calloc  :: proc (size: u32) -> rawptr do return raw_alloc(context.allocator, size);
+cresize :: proc (ptr: rawptr, size: u32) -> rawptr do return raw_resize(context.allocator, ptr, size);
+cfree   :: proc (ptr: rawptr) do raw_free(context.allocator, ptr);
+
 
 // @CLEANUP: These need to move to somewhere else eventually
 cmp_asc :: proc (a: $T, b: T) -> i32 do return cast(i32) (a - b);
 cmp_dec :: proc (a: $T, b: T) -> i32 do return cast(i32) (b - a);
 
-context : struct {
-    allocator      : Allocator;
-    temp_allocator : Allocator;
-}
index da9381ff925263513f15fc4b6618e82d9f0cc9bb..1499fa5f62788406a4ffb5586525473573efc3eb 100644 (file)
@@ -122,7 +122,7 @@ get_size :: proc (file: File) -> u64 {
 get_contents_from_file :: proc (file: File) -> str {
     size := cast(u32) get_size(file);
 
-    data := cast(^u8) alloc(context.allocator, size);
+    data := cast(^u8) raw_alloc(context.allocator, size);
 
     prev_loc: i64;
     fd_tell(file.fd, ^prev_loc);
index e1635677deadc7e7d69e7ad2c3bb711a57bb2736..4f502f91eb7318c6d1744e3ac54773986a54cecb 100644 (file)
@@ -8,10 +8,10 @@ output_str :: proc (s: str) -> u32 #foreign "host" "print_str" ---
 // The builtin _start proc.
 // Sets up everything needed for execution.
 proc () #export "_start" {
-    allocator.heap_init();
+    alloc.init();
 
-    context.allocator = allocator.heap_allocator;
-    context.temp_allocator = allocator.heap_allocator;
+    context.allocator = alloc.heap_allocator;
+    context.temp_allocator = alloc.temp_allocator;
 
     args : [] cstr;
     args.data  = null;
index 0dbd2c7c7f1153d7ae315494de047b828587a36d..95c2893dc0e3f3f6b0df8e45a8e65091f066fe53 100644 (file)
@@ -19,10 +19,10 @@ output_str :: proc (s: str) -> u32 {
 // The builtin _start proc.
 // Sets up everything needed for execution.
 proc () #export "_start" {
-    allocator.heap_init();
+    alloc.init();
 
-    context.allocator = allocator.heap_allocator;
-    context.temp_allocator = allocator.heap_allocator;
+    context.allocator = alloc.heap_allocator;
+    context.temp_allocator = alloc.heap_allocator;
 
     argc : Size;
     argv_buf_size : Size;
diff --git a/onyx b/onyx
index 7e4f8e784411ae235b607d6681ccdd2a0420641a..94eaf2bc5177bb50869e37e48c495bf12f844b5b 100755 (executable)
Binary files a/onyx and b/onyx differ
index 5a885f826dc90c93506a142581298dd78e5f56dd..5003c71ad2234b544ea9f369ca44cb6dbd740c5b 100644 (file)
@@ -379,8 +379,10 @@ static void symres_array_literal(AstArrayLiteral* al) {
     bh_arr_each(AstTyped *, expr, al->values)
         symres_expression(expr);
 
-    bh_arr_push(bh_arr_last(semstate.block_stack)->allocate_exprs, (AstTyped *) al);
-    bh_arr_push(semstate.curr_function->allocate_exprs, (AstTyped *) al);
+    if (bh_arr_length(semstate.block_stack) > 0) {
+        bh_arr_push(bh_arr_last(semstate.block_stack)->allocate_exprs, (AstTyped *) al);
+        bh_arr_push(semstate.curr_function->allocate_exprs, (AstTyped *) al);
+    }
 }
 
 static void symres_expression(AstTyped** expr) {
index c96ebb44b9b457e8756366d785ed7e25181b1b0b..f3a8fd367ef699eae3f542ca2a8553016b66b716 100644 (file)
@@ -21,7 +21,7 @@ Evens from 6 to 34:
 Array details:
        Size: 0
        Capacity: 4
-       Data ptr: 0x10A48
+       Data ptr: 0x11A68
        Size of elements: 4
        Alignment of elements: 4
 
@@ -29,7 +29,7 @@ Array details:
 Array details:
        Size: 0
        Capacity: 4
-       Data ptr: 0x10A68
+       Data ptr: 0x11A88
        Size of elements: 8
        Alignment of elements: 8
 
@@ -37,8 +37,8 @@ Array details:
 0 5 10 15 20 4 9 14 19 3 8 13 18 2 7 12 17 1 6 11 16 0 5 10 15 20 4 9 14 19 3 8 13 18 2 7 12 17 1 6 11 16 0 5 10 15 20 4 9 14 19 3 8 13 18 2 7 12 17 1 6 11 16 0 5 10 15 20 4 9 14 19 3 8 13 18 2 7 12 17 1 6 11 16 0 5 10 15 20 4 9 14 19 3 8 13 18 2 7 12
 A has 22? false
 Vec3(0, 0, 0) Vec3(1, 1, 1) Vec3(2, 4, 8) Vec3(3, 9, 27) Vec3(4, 16, 64) Vec3(5, 25, 125) Vec3(6, 36, 216) Vec3(7, 49, 343) Vec3(8, 64, 512) Vec3(9, 81, 729) Vec3(10, 100, 1000) Vec3(11, 121, 1331) Vec3(12, 144, 1728) Vec3(13, 169, 2197) Vec3(14, 196, 2744) Vec3(15, 225, 3375) Vec3(16, 256, 4096) Vec3(17, 289, 4913) Vec3(18, 324, 5832) Vec3(19, 361, 6859) Vec3(20, 400, 8000) Vec3(21, 441, 9261) Vec3(22, 484, 10648) Vec3(23, 529, 12167) Vec3(24, 576, 13824) Vec3(25, 625, 15625) Vec3(26, 676, 17576) Vec3(27, 729, 19683) Vec3(28, 784, 21952) Vec3(29, 841, 24389) Vec3(30, 900, 27000) Vec3(31, 961, 29791) Vec3(32, 1024, 32768) Vec3(33, 1089, 35937) Vec3(34, 1156, 39304) Vec3(35, 1225, 42875) Vec3(36, 1296, 46656) Vec3(37, 1369, 50653) Vec3(38, 1444, 54872) Vec3(39, 1521, 59319) Vec3(40, 1600, 64000) Vec3(41, 1681, 68921) Vec3(42, 1764, 74088) Vec3(43, 1849, 79507) Vec3(44, 1936, 85184) Vec3(45, 2025, 91125) Vec3(46, 2116, 97336) Vec3(47, 2209, 103823) Vec3(48, 2304, 110592) Vec3(49, 2401, 117649) Vec3(50, 2500, 125000) Vec3(51, 2601, 132651) Vec3(52, 2704, 140608) Vec3(53, 2809, 148877) Vec3(54, 2916, 157464) Vec3(55, 3025, 166375) Vec3(56, 3136, 175616) Vec3(57, 3249, 185193) Vec3(58, 3364, 195112) Vec3(59, 3481, 205379) Vec3(60, 3600, 216000) Vec3(61, 3721, 226981) Vec3(62, 3844, 238328) Vec3(63, 3969, 250047) Vec3(64, 4096, 262144) Vec3(65, 4225, 274625) Vec3(66, 4356, 287496) Vec3(67, 4489, 300763) Vec3(68, 4624, 314432) Vec3(69, 4761, 328509) Vec3(70, 4900, 343000) Vec3(71, 5041, 357911) Vec3(72, 5184, 373248) Vec3(73, 5329, 389017) Vec3(74, 5476, 405224) Vec3(75, 5625, 421875) Vec3(76, 5776, 438976) Vec3(77, 5929, 456533) Vec3(78, 6084, 474552) Vec3(79, 6241, 493039) Vec3(80, 6400, 512000) Vec3(81, 6561, 531441) Vec3(82, 6724, 551368) Vec3(83, 6889, 571787) Vec3(84, 7056, 592704) Vec3(85, 7225, 614125) Vec3(86, 7396, 636056) Vec3(87, 7569, 658503) Vec3(88, 7744, 681472) Vec3(89, 7921, 704969) Vec3(90, 8100, 729000) Vec3(91, 8281, 753571) Vec3(92, 8464, 778688) Vec3(93, 8649, 804357) Vec3(94, 8836, 830584) Vec3(95, 9025, 857375) Vec3(96, 9216, 884736) Vec3(97, 9409, 912673) Vec3(98, 9604, 941192) Vec3(99, 9801, 970299) 
-0x11CF8 0x11D04 0x11D10 0x11D1C 0x11D28 0x11D34 0x11D40 0x11D4C 0x11D58 0x11D64 0x11D70 0x11D7C 0x11D88 0x11D94 0x11DA0 0x11DAC 0x11DB8 0x11DC4 0x11DD0 0x11DDC 0x11DE8 0x11DF4 0x11E00 0x11E0C 0x11E18 0x11E24 0x11E30 0x11E3C 0x11E48 0x11E54 0x11E60 0x11E6C 0x11E78 0x11E84 0x11E90 0x11E9C 0x11EA8 0x11EB4 0x11EC0 0x11ECC 0x11ED8 0x11EE4 0x11EF0 0x11EFC 0x11F08 0x11F14 0x11F20 0x11F2C 0x11F38 0x11F44 0x11F50 0x11F5C 0x11F68 0x11F74 0x11F80 0x11F8C 0x11F98 0x11FA4 0x11FB0 0x11FBC 0x11FC8 0x11FD4 0x11FE0 0x11FEC 0x11FF8 0x12004 0x12010 0x1201C 0x12028 0x12034 0x12040 0x1204C 0x12058 0x12064 0x12070 0x1207C 0x12088 0x12094 0x120A0 0x120AC 0x120B8 0x120C4 0x120D0 0x120DC 0x120E8 0x120F4 0x12100 0x1210C 0x12118 0x12124 0x12130 0x1213C 0x12148 0x12154 0x12160 0x1216C 0x12178 0x12184 0x12190 0x1219
-1870 1874 1878 1882 1886 1890 1894 1898 1902 1906 1910 1914 
+0x12D18 0x12D24 0x12D30 0x12D3C 0x12D48 0x12D54 0x12D60 0x12D6C 0x12D78 0x12D84 0x12D90 0x12D9C 0x12DA8 0x12DB4 0x12DC0 0x12DCC 0x12DD8 0x12DE4 0x12DF0 0x12DFC 0x12E08 0x12E14 0x12E20 0x12E2C 0x12E38 0x12E44 0x12E50 0x12E5C 0x12E68 0x12E74 0x12E80 0x12E8C 0x12E98 0x12EA4 0x12EB0 0x12EBC 0x12EC8 0x12ED4 0x12EE0 0x12EEC 0x12EF8 0x12F04 0x12F10 0x12F1C 0x12F28 0x12F34 0x12F40 0x12F4C 0x12F58 0x12F64 0x12F70 0x12F7C 0x12F88 0x12F94 0x12FA0 0x12FAC 0x12FB8 0x12FC4 0x12FD0 0x12FDC 0x12FE8 0x12FF4 0x13000 0x1300C 0x13018 0x13024 0x13030 0x1303C 0x13048 0x13054 0x13060 0x1306C 0x13078 0x13084 0x13090 0x1309C 0x130A8 0x130B4 0x130C0 0x130CC 0x130D8 0x130E4 0x130F0 0x130FC 0x13108 0x13114 0x13120 0x1312C 0x13138 0x13144 0x13150 0x1315C 0x13168 0x13174 0x13180 0x1318C 0x13198 0x131A4 0x131B0 0x131B
+1886 1890 1894 1898 1902 1906 1910 1914 1918 1922 1926 1930 
 20 20 20 20 20 19 19 19 19 19 18 18 18 18 18 17 17 17 17 16 16 16 16 15 15 15 15 15 14 14 14 14 14 13 13 13 13 13 12 12 12 12 12 11 11 11 11 10 10 10 10 10 9 9 9 9 9 8 8 8 8 8 7 7 7 7 7 6 6 6 6 5 5 5 5 5 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 1 1 1 1 0 0 0 0 0
 297 294 291 288 285 282 279 276 273 270 267 264 261 258 255 252 249 246 243 240 237 234 231 228 225 222 219 216 213 210 207 204 201 198 195 192 189 186 183 180 177 174 171 168 165 162 159 156 153 150 147 144 141 138 135 132 129 126 123 120 117 114 111 108 105 102 99 96 93 90 87 84 81 78 75 72 69 66 63 60 57 54 51 48 45 42 39 36 33 30 27 24 21 18 15 12 9 6 3 0
 After adding...
@@ -46,7 +46,7 @@ After adding...
 Array details:
        Size: 100
        Capacity: 128
-       Data ptr: 0x116D8
+       Data ptr: 0x126F8
        Size of elements: 4
        Alignment of elements: 4
 
@@ -54,7 +54,7 @@ Array details:
 Array details:
        Size: 100
        Capacity: 128
-       Data ptr: 0x118E8
+       Data ptr: 0x12908
        Size of elements: 8
        Alignment of elements: 8
 
@@ -62,7 +62,7 @@ Array A sum: 999
 
 Has ^a[20]? true
 Has null? false
-Value at ^a[50]: 0x11A78 == 0x11A78
+Value at ^a[50]: 0x12A98 == 0x12A98
 Deleteing ^a[20]
 Has ^a[20]? false
 Clearing SOA...