diff --git a/_run_all_tests.jai b/_run_all_tests.jai index 0a2110d..df80cc6 100644 --- a/_run_all_tests.jai +++ b/_run_all_tests.jai @@ -18,5 +18,3 @@ tmath :: #import,file "./math/module.jai"(.turns, RUN_TESTS = true); } - - diff --git a/array/stable_array.jai b/array/stable_array.jai index 3fdbfab..6299dee 100644 --- a/array/stable_array.jai +++ b/array/stable_array.jai @@ -1,9 +1,13 @@ -Stable_Array :: struct(T: Type, ITEMS_PER_CHUNK := 32) { +// A dynamic array whose values will never move in memory. +// +// This means it is safe to take a pointer to a value within the array +// while continuing to append to it. +Stable_Array :: struct(T: Type, items_per_chunk := 32) { allocator: Allocator; chunks: [..]*Chunk; count: int; - Chunk :: Static_Array(ITEMS_PER_CHUNK, T); + Chunk :: Static_Array(items_per_chunk, T); } init :: (a: *Stable_Array, allocator: Allocator) { @@ -13,9 +17,8 @@ init :: (a: *Stable_Array, allocator: Allocator) { append :: (a: *Stable_Array) -> *a.T { chunk := find_or_create_chunk(a, 1); - item := append(chunk); a.count += 1; - return item; + return append(chunk); } append :: (a: *Stable_Array, value: a.T) -> *a.T { @@ -26,6 +29,9 @@ append :: (a: *Stable_Array, value: a.T) -> *a.T { } append :: (a: *Stable_Array, values: ..a.T) -> *a.T { + // @todo(judah): this should look for chunks where can just copy values directly + // rather than calling append for each one. + first: *a.T; for values { if first == null { @@ -46,42 +52,47 @@ reset :: (a: *Stable_Array) { } operator [] :: (a: Stable_Array, index: int, loc := #caller_location) -> a.T #no_abc { - b_idx := index / a.ITEMS_PER_CHUNK; - i_idx := index % a.ITEMS_PER_CHUNK; - meta.check_bounds(b_idx, a.chunks.count, loc = loc); - meta.check_bounds(i_idx, a.chunks[b_idx].count, loc = loc); - return a.chunks[b_idx].items[i_idx]; + cidx := index / a.items_per_chunk; + iidx := index % a.items_per_chunk; + meta.check_bounds(cidx, a.chunks.count, loc = loc); + meta.check_bounds(iidx, a.chunks[cidx].count, loc = loc); + return a.chunks[cidx].items[iidx]; } operator *[] :: (a: *Stable_Array, index: int, loc := #caller_location) -> *a.T #no_abc { - b_idx := index / a.ITEMS_PER_CHUNK; - i_idx := index % a.ITEMS_PER_CHUNK; - meta.check_bounds(b_idx, a.chunks.count, loc = loc); - meta.check_bounds(i_idx, a.chunks[b_idx].count, loc = loc); - return *a.chunks[b_idx].items[i_idx]; + cidx := index / a.items_per_chunk; + iidx := index % a.items_per_chunk; + meta.check_bounds(cidx, a.chunks.count, loc = loc); + meta.check_bounds(iidx, a.chunks[cidx].count, loc = loc); + return *a.chunks[cidx].items[iidx]; } operator []= :: (a: *Stable_Array, index: int, value: a.T, loc := #caller_location) #no_abc { - c_idx := index / a.ITEMS_PER_CHUNK; - i_idx := index % a.ITEMS_PER_CHUNK; - meta.check_bounds(c_idx, a.chunks.count, loc = loc); - meta.check_bounds(i_idx, a.chunks[b_idx].count, loc = loc); - - chunk := a.chunks[c_idx]; - chunk.items[i_idx] = value; + cidx := index / a.items_per_chunk; + iidx := index % a.items_per_chunk; + meta.check_bounds(cidx, a.chunks.count, loc = loc); + meta.check_bounds(iidx, a.chunks[cidx].count, loc = loc); + a.chunks[cidx].items[iidx] = value; } for_expansion :: (a: Stable_Array, body: Code, flags: For_Flags) #expand { - for i: 0..a.count - 1 { - `it := a[i]; + for #v2 <=(flags & .REVERSE == .REVERSE) i: 0..a.count - 1 { `it_index := i; + #if flags & .POINTER == .POINTER { + `it := *a[i]; + } + else { + `it := a[i]; + } + #insert,scope(body) body; } } + #scope_file; -mem :: #import "jc/memory"; +mem :: #import "jc/memory"; meta :: #import "jc/meta"; find_or_create_chunk :: (a: *Stable_Array, amount: int) -> *a.Chunk { @@ -90,7 +101,7 @@ find_or_create_chunk :: (a: *Stable_Array, amount: int) -> *a.Chunk { } last := a.chunks[a.chunks.count - 1]; - if amount > a.ITEMS_PER_CHUNK - last.count { + if amount > a.items_per_chunk - last.count { last = create_chunk(a); } @@ -111,24 +122,60 @@ try_lazy_init :: (a: *Stable_Array) { } } -// #run { -// #import "Basic"; -// { -// a: Stable_Array(int); -// for 0..64 { -// append(*a, it * it); -// } +// ---------------------------------------------------------- +// TESTS +// ---------------------------------------------------------- -// reset(*a); +basic :: #import "Basic"; -// append(*a, 10); -// append(*a, 20); -// append(*a, 30); +#if RUN_TESTS #run { + test :: #import "jc/test"; -// for a { -// print("%: %\n", it_index, it); -// } -// } -// } + test.run("basic operations", t => { + a: Stable_Array(int, 4); + append(*a, 10, 20, 30, 40); + test.expect(t, a.count == 4); + test.expect(t, a.chunks.count == 1, "chunk count was %", a.chunks.count); + + append(*a, 50); + test.expect(t, a.count == 5); + test.expect(t, a.chunks.count == 2, "chunk count was %", a.chunks.count); + + append(*a, 60, 70, 80, 90, 100, 110, 120); + test.expect(t, a.count == 12); + test.expect(t, a.chunks.count == 3, "chunk count was %", a.chunks.count); + + for a { + test.expect(t, it == (it_index + 1) * 10, "% was %", it, (it_index + 1) * 10); + } + }); + + test.run("iteration", t => { + a: Stable_Array(int); + append(*a, 10, 20, 30, 40); + + last := 999; + for < a { + test.expect(t, it == (it_index + 1) * 10); + test.expect(t, it < last); + + last = it; + } + + for * a it.* = 1; + for a test.expect(t, it == 1); + }); + + test.run("stability", t => { + a: Stable_Array(int, 1); + + first := append(*a, 10); + addr := first.(u64); + for 0..10 append(*a, it * 10); + + test.expect(t, first.(u64) == addr); + test.expect(t, first.* == 10); + }); +}