#module_parameters(RUN_TESTS := false); // 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); } append :: (a: *Stable_Array) -> *a.T { chunk := find_or_create_chunk(a, 1); a.count += 1; return append(chunk); } append :: (a: *Stable_Array, value: a.T) -> *a.T { chunk := find_or_create_chunk(a, 1); item := append(chunk, value); a.count += 1; return item; } 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 { first = inline append(a, it); } else { inline append(a, it); } } return first; } reset :: (a: *Stable_Array) { for a.chunks it.count = 0; a.count = 0; a.chunks.count = 0; } find :: (a: Stable_Array, $predicate: (a.T) -> bool) -> a.T, bool, int { for a if inline predicate(it) return it, true, it_index; return mem.undefined_of(a.T), false, -1; } find_pointer :: (a: *Stable_Array, $predicate: (a.T) -> bool) -> *a.T, bool, int { for * a if inline predicate(it.*) return it, true, it_index; return null, false, -1; } operator [] :: (a: Stable_Array, index: int, loc := #caller_location) -> a.T #no_abc { 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 { 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 { 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 #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; find_or_create_chunk :: (a: *Stable_Array, amount: int) -> *a.Chunk { if a.chunks.count == 0 { return create_chunk(a); } last := a.chunks[a.chunks.count - 1]; if amount > a.items_per_chunk - last.count { last = create_chunk(a); } return last; } create_chunk :: (a: *Stable_Array) -> *a.Chunk { mem.lazy_set_allocator(a); mem.lazy_set_allocator(*a.chunks); chunk := mem.request_memory(a.Chunk,, allocator = a.allocator); append(*a.chunks, chunk); return chunk; } #import "jc/array"; mem :: #import "jc/memory"; meta :: #import "jc/meta"; // ---------------------------------------------------------- // TESTS // ---------------------------------------------------------- #if RUN_TESTS #run { test :: #import "jc/meta/test"; 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); ptr, ok, idx := find_pointer(*a, v => v == 1); test.expect(t, ok); test.expect(t, idx == 0); test.expect(t, ptr == *a[0]); a[a.count - 1] = -1; _, ok, idx = find(a, v => v == -1); test.expect(t, ok); test.expect(t, idx == a.count - 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); }); }