From: Brendan Hansen Date: Wed, 21 Apr 2021 13:35:50 +0000 (-0500) Subject: moved container-like things to core/container X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fdd685fb72d100e21c91a980673d0e6a296ee3fd;p=onyx.git moved container-like things to core/container --- diff --git a/core/array.onyx b/core/array.onyx deleted file mode 100644 index 20f5ac38..00000000 --- a/core/array.onyx +++ /dev/null @@ -1,243 +0,0 @@ -package core.array - -// [..] T == Array(T) -// where -// Array :: struct (T: type_expr) { -// data : T; -// count : u32; -// capacity : u32; -// allocator : Allocator; -// } - -// --------------------------------- -// Dynamic Arrays -// --------------------------------- -make :: ($T: type_expr, capacity := 4, allocator := context.allocator) -> [..] T { - arr : [..] T; - init(^arr, capacity, allocator); - return arr; -} - -init :: (arr: ^[..] $T, capacity := 4, allocator := context.allocator) { - arr.count = 0; - arr.capacity = capacity; - arr.allocator = allocator; - arr.data = raw_alloc(allocator, sizeof T * arr.capacity); -} - -free :: (arr: ^[..] $T) { - arr.count = 0; - arr.capacity = 0; - - raw_free(arr.allocator, arr.data); - arr.data = null; -} - -copy :: (arr: ^[..] $T, allocator := context.allocator) -> [..] T { - new_arr : [..] T; - init(^new_arr, arr.count, allocator); - new_arr.count = arr.count; - - for i: 0 .. arr.count do new_arr.data[i] = arr.data[i]; - return new_arr; -} - -copy_range :: (arr: ^[..] $T, r: range, allocator := context.allocator) -> [..] T { - new_arr : [..] T; - init(^new_arr, r.high - r.low, allocator); - new_arr.count = r.high - r.low; - - for i: r do new_arr.data[i] = arr.data[i]; - return new_arr; -} - -clear :: (arr: ^[..] $T) { - arr.count = 0; -} - -ensure_capacity :: (arr: ^[..] $T, capacity: u32) -> bool { - if arr.capacity >= capacity do return true; - - while capacity > arr.capacity do arr.capacity <<= 1; - new_data := raw_resize(arr.allocator, arr.data, sizeof T * arr.capacity); - if new_data == null do return false; - arr.data = new_data; - return true; -} - -push :: (arr: ^[..] $T, x: T) -> bool { - if !ensure_capacity(arr, arr.count + 1) do return false; - arr.data[arr.count] = x; - arr.count += 1; - return true; -} - -insert :: (arr: ^[..] $T, idx: u32, x: T) -> bool { - if !ensure_capacity(arr, arr.count + 1) do return false; - - arr.count += 1; - while i := arr.count; i > idx { - arr.data[i] = arr.data[i - 1]; - i -= 1; - } - - arr.data[idx] = x; - return true; -} - -insert_empty :: (arr: ^[..] $T, idx: u32) -> bool { - if !ensure_capacity(arr, arr.count + 1) do return false; - - arr.count += 1; - while i := arr.count; i > idx { - arr.data[i] = arr.data[i - 1]; - i -= 1; - } - - return true; -} - -remove :: (arr: ^[..] $T, elem: T) { - move := 0; - - while i := 0; i < arr.count - move { - defer i += 1; - - if arr.data[i + move] == elem do move += 1; - if move != 0 do arr.data[i] = arr.data[i + move]; - } - - arr.count -= move; -} - -delete :: (arr: ^[..] $T, idx: u32) { - if idx >= arr.count do return; - - for i: idx .. arr.count - 1 { - arr.data[i] = arr.data[i + 1]; - } - - arr.count -= 1; -} - -fast_delete :: (arr: ^[..] $T, idx: u32) { - if idx >= arr.count do return; - - arr.data[idx] = arr.data[arr.count - 1]; - arr.count -= 1; -} - -pop :: (arr: ^[..] $T) -> T { - arr.count -= 1; - return arr.data[arr.count]; -} - -// Uses '==' to compare for equality. -contains :: proc { - (arr: ^[..] $T, x: T) -> bool { - for it: *arr do if it == x do return true; - return false; - }, - - (arr: [] $T, x: T) -> bool { - for it: arr do if it == x do return true; - return false; - } -} - -// Uses '+' to sum. -sum :: proc { - (arr: ^[..] $T, start: T = 0) -> T { - sum := start; - for it: *arr do sum += it; - return sum; - }, - - (arr: [] $T, start: T = 0) -> T { - sum := start; - for it: arr do sum += it; - return sum; - } -} - -average :: (arr: ^[..] $T) -> T { - sum := cast(T) 0; - for it: *arr do sum += it; - - return sum / cast(T) arr.count; -} - -to_slice :: (arr: ^[..] $T) -> [] T { - return arr.data[0 .. arr.count]; -} - -/* -** Simple insertion sort -** cmp should return >0 if left > right -*/ -sort :: (arr: ^[..] $T, cmp: (T, T) -> i32) { - for i: 1 .. arr.count { - x := arr.data[i]; - j := i - 1; - - while j >= 0 && cmp(arr.data[j], x) > 0 { - arr.data[j + 1] = arr.data[j]; - j -= 1; - } - - arr.data[j + 1] = x; - } -} - -fold :: proc { - (arr: ^[..] $T, init: $R, f: (T, R) -> R) -> R { - val := init; - for it: *arr do val = f(it, val); - return val; - }, - - (arr: [] $T, init: $R, f: (T, R) -> R) -> R { - val := init; - for it: arr do val = f(it, val); - return val; - } -} - -map :: proc { - (arr: ^[..] $T, f: (^T) -> void) do for ^it: *arr do f(it);, - (arr: ^[..] $T, f: (T) -> T) do for ^it: *arr do *it = f(*it);, - (arr: ^[..] $T, data: $R, f: (T, R) -> T) do for ^it: *arr do *it = f(*it, data);, - (arr: [] $T, f: (^T) -> void) do for ^it: arr do f(it);, - (arr: [] $T, f: (T) -> T) do for ^it: arr do *it = f(*it);, - (arr: [] $T, data: $R, f: (T, R) -> T) do for ^it: arr do *it = f(*it, data);, -} - -#private_file -fold_idx_elem :: (arr: ^$T, count: i32, cmp: (T, T) -> bool) -> (i32, T) { - idx := 0; - elem := arr[0]; - - for i: 1 .. count { - if cmp(arr[i], elem) { - idx = i; - elem = arr[i]; - } - } - - return idx, elem; -} - -#private_file cmp_greater :: (x: $T, y: T) -> bool do return x > y; -#private_file cmp_less :: (x: $T, y: T) -> bool do return x < y; - -greatest :: proc { - (arr: [..] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_greater); }, - (arr: [] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_greater); }, - (arr: [$N] $T) -> (i32, T) { return fold_idx_elem(cast(^T) arr, N, cmp_greater); }, -} - -least :: proc { - (arr: [..] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_less); }, - (arr: [] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_less); }, - (arr: [$N] $T) -> (i32, T) { return fold_idx_elem(cast(^T) arr, N, cmp_less); }, -} diff --git a/core/container/array.onyx b/core/container/array.onyx new file mode 100644 index 00000000..20f5ac38 --- /dev/null +++ b/core/container/array.onyx @@ -0,0 +1,243 @@ +package core.array + +// [..] T == Array(T) +// where +// Array :: struct (T: type_expr) { +// data : T; +// count : u32; +// capacity : u32; +// allocator : Allocator; +// } + +// --------------------------------- +// Dynamic Arrays +// --------------------------------- +make :: ($T: type_expr, capacity := 4, allocator := context.allocator) -> [..] T { + arr : [..] T; + init(^arr, capacity, allocator); + return arr; +} + +init :: (arr: ^[..] $T, capacity := 4, allocator := context.allocator) { + arr.count = 0; + arr.capacity = capacity; + arr.allocator = allocator; + arr.data = raw_alloc(allocator, sizeof T * arr.capacity); +} + +free :: (arr: ^[..] $T) { + arr.count = 0; + arr.capacity = 0; + + raw_free(arr.allocator, arr.data); + arr.data = null; +} + +copy :: (arr: ^[..] $T, allocator := context.allocator) -> [..] T { + new_arr : [..] T; + init(^new_arr, arr.count, allocator); + new_arr.count = arr.count; + + for i: 0 .. arr.count do new_arr.data[i] = arr.data[i]; + return new_arr; +} + +copy_range :: (arr: ^[..] $T, r: range, allocator := context.allocator) -> [..] T { + new_arr : [..] T; + init(^new_arr, r.high - r.low, allocator); + new_arr.count = r.high - r.low; + + for i: r do new_arr.data[i] = arr.data[i]; + return new_arr; +} + +clear :: (arr: ^[..] $T) { + arr.count = 0; +} + +ensure_capacity :: (arr: ^[..] $T, capacity: u32) -> bool { + if arr.capacity >= capacity do return true; + + while capacity > arr.capacity do arr.capacity <<= 1; + new_data := raw_resize(arr.allocator, arr.data, sizeof T * arr.capacity); + if new_data == null do return false; + arr.data = new_data; + return true; +} + +push :: (arr: ^[..] $T, x: T) -> bool { + if !ensure_capacity(arr, arr.count + 1) do return false; + arr.data[arr.count] = x; + arr.count += 1; + return true; +} + +insert :: (arr: ^[..] $T, idx: u32, x: T) -> bool { + if !ensure_capacity(arr, arr.count + 1) do return false; + + arr.count += 1; + while i := arr.count; i > idx { + arr.data[i] = arr.data[i - 1]; + i -= 1; + } + + arr.data[idx] = x; + return true; +} + +insert_empty :: (arr: ^[..] $T, idx: u32) -> bool { + if !ensure_capacity(arr, arr.count + 1) do return false; + + arr.count += 1; + while i := arr.count; i > idx { + arr.data[i] = arr.data[i - 1]; + i -= 1; + } + + return true; +} + +remove :: (arr: ^[..] $T, elem: T) { + move := 0; + + while i := 0; i < arr.count - move { + defer i += 1; + + if arr.data[i + move] == elem do move += 1; + if move != 0 do arr.data[i] = arr.data[i + move]; + } + + arr.count -= move; +} + +delete :: (arr: ^[..] $T, idx: u32) { + if idx >= arr.count do return; + + for i: idx .. arr.count - 1 { + arr.data[i] = arr.data[i + 1]; + } + + arr.count -= 1; +} + +fast_delete :: (arr: ^[..] $T, idx: u32) { + if idx >= arr.count do return; + + arr.data[idx] = arr.data[arr.count - 1]; + arr.count -= 1; +} + +pop :: (arr: ^[..] $T) -> T { + arr.count -= 1; + return arr.data[arr.count]; +} + +// Uses '==' to compare for equality. +contains :: proc { + (arr: ^[..] $T, x: T) -> bool { + for it: *arr do if it == x do return true; + return false; + }, + + (arr: [] $T, x: T) -> bool { + for it: arr do if it == x do return true; + return false; + } +} + +// Uses '+' to sum. +sum :: proc { + (arr: ^[..] $T, start: T = 0) -> T { + sum := start; + for it: *arr do sum += it; + return sum; + }, + + (arr: [] $T, start: T = 0) -> T { + sum := start; + for it: arr do sum += it; + return sum; + } +} + +average :: (arr: ^[..] $T) -> T { + sum := cast(T) 0; + for it: *arr do sum += it; + + return sum / cast(T) arr.count; +} + +to_slice :: (arr: ^[..] $T) -> [] T { + return arr.data[0 .. arr.count]; +} + +/* +** Simple insertion sort +** cmp should return >0 if left > right +*/ +sort :: (arr: ^[..] $T, cmp: (T, T) -> i32) { + for i: 1 .. arr.count { + x := arr.data[i]; + j := i - 1; + + while j >= 0 && cmp(arr.data[j], x) > 0 { + arr.data[j + 1] = arr.data[j]; + j -= 1; + } + + arr.data[j + 1] = x; + } +} + +fold :: proc { + (arr: ^[..] $T, init: $R, f: (T, R) -> R) -> R { + val := init; + for it: *arr do val = f(it, val); + return val; + }, + + (arr: [] $T, init: $R, f: (T, R) -> R) -> R { + val := init; + for it: arr do val = f(it, val); + return val; + } +} + +map :: proc { + (arr: ^[..] $T, f: (^T) -> void) do for ^it: *arr do f(it);, + (arr: ^[..] $T, f: (T) -> T) do for ^it: *arr do *it = f(*it);, + (arr: ^[..] $T, data: $R, f: (T, R) -> T) do for ^it: *arr do *it = f(*it, data);, + (arr: [] $T, f: (^T) -> void) do for ^it: arr do f(it);, + (arr: [] $T, f: (T) -> T) do for ^it: arr do *it = f(*it);, + (arr: [] $T, data: $R, f: (T, R) -> T) do for ^it: arr do *it = f(*it, data);, +} + +#private_file +fold_idx_elem :: (arr: ^$T, count: i32, cmp: (T, T) -> bool) -> (i32, T) { + idx := 0; + elem := arr[0]; + + for i: 1 .. count { + if cmp(arr[i], elem) { + idx = i; + elem = arr[i]; + } + } + + return idx, elem; +} + +#private_file cmp_greater :: (x: $T, y: T) -> bool do return x > y; +#private_file cmp_less :: (x: $T, y: T) -> bool do return x < y; + +greatest :: proc { + (arr: [..] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_greater); }, + (arr: [] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_greater); }, + (arr: [$N] $T) -> (i32, T) { return fold_idx_elem(cast(^T) arr, N, cmp_greater); }, +} + +least :: proc { + (arr: [..] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_less); }, + (arr: [] $T) -> (i32, T) { return fold_idx_elem(arr.data, arr.count, cmp_less); }, + (arr: [$N] $T) -> (i32, T) { return fold_idx_elem(cast(^T) arr, N, cmp_less); }, +} diff --git a/core/container/iter.onyx b/core/container/iter.onyx new file mode 100644 index 00000000..ff8f0297 --- /dev/null +++ b/core/container/iter.onyx @@ -0,0 +1,194 @@ +package core.iter + +use package core.intrinsics.onyx { __zero_value } + +filter :: (it: Iterator($T), predicate: (T) -> bool) -> Iterator(T) { + FilterIterator :: struct (T: type_expr) { + iterator: Iterator(T); + predicate: (T) -> bool; + } + + filter_iterator := new(#type FilterIterator(T)); + filter_iterator.iterator = it; + filter_iterator.predicate = predicate; + + next :: ($T: type_expr, data: rawptr) -> (T, bool) { + fi := cast(^FilterIterator(T)) data; + + value, cont := fi.iterator.next(fi.iterator.data); + if cont { + while !fi.predicate(value) { + value, cont = fi.iterator.next(fi.iterator.data); + if !cont do return value, false; + } + + return value, true; + } else { + return value, false; + } + } + + close :: ($T: type_expr, data: rawptr) { + fi := cast(^FilterIterator(T)) data; + fi.iterator.close(fi.iterator.data); + cfree(data); + } + + return .{ + data = filter_iterator, + next = #solidify next { T=T }, + close = #solidify close { T=T }, + }; +} + +map :: (it: Iterator($T), transform: (T) -> $R) -> Iterator(R) { + MapIterator :: struct (T: type_expr, R: type_expr) { + iterator: Iterator(T); + transform: (T) -> R; + } + + map_iterator := new(#type MapIterator(T, R)); + map_iterator.iterator = it; + map_iterator.transform = transform; + + next :: ($T: type_expr, $R: type_expr, data: rawptr) -> (R, bool) { + mi := cast(^MapIterator(T, R)) data; + + value, cont := mi.iterator.next(mi.iterator.data); + if !cont do return value, false; + + return mi.transform(value), true; + } + + close :: ($T: type_expr, $R: type_expr, data: rawptr) { + mi := cast(^MapIterator(T, R)) data; + mi.iterator.close(mi.iterator.data); + cfree(data); + } + + return .{ + data = map_iterator, + next = #solidify next { T=T, R=R }, + close = #solidify close { T=T, R=R }, + }; +} + +take :: (it: Iterator($T), count: u32) -> Iterator(T) { + TakeIterator :: struct (T: type_expr) { + iterator: Iterator(T); + remaining: u32; + } + + take_iterator := new(#type TakeIterator(T)); + take_iterator.iterator = it; + take_iterator.remaining = count; + + next :: ($T: type_expr, data: rawptr) -> (T, bool) { + ti := cast(^TakeIterator(T)) data; + if ti.remaining == 0 do return __zero_value(T), false; + + ti.remaining -= 1; + return ti.iterator.next(ti.iterator.data); + } + + close :: ($T: type_expr, data: rawptr) { + ti := cast(^TakeIterator(T)) data; + ti.iterator.close(ti.iterator.data); + cfree(data); + } + + return .{ + data = take_iterator, + next = #solidify next { T=T }, + close = #solidify close { T=T }, + }; +} + +take_while :: (it: Iterator($T), predicate: (T) -> bool) -> Iterator(T) { + TakeIterator :: struct (T: type_expr) { + iterator: Iterator(T); + predicate: (T) -> bool; + } + + take_iterator := new(#type TakeIterator(T)); + take_iterator.iterator = it; + take_iterator.predicate = predicate; + + next :: ($T: type_expr, data: rawptr) -> (T, bool) { + ti := cast(^TakeIterator(T)) data; + + value, cont := ti.iterator.next(ti.iterator.data); + if !cont do return value, false; + + return value, ti.predicate(value); + } + + close :: ($T: type_expr, data: rawptr) { + ti := cast(^TakeIterator(T)) data; + ti.iterator.close(ti.iterator.data); + cfree(data); + } + + return .{ + data = take_iterator, + next = #solidify next { T=T }, + close = #solidify close { T=T }, + }; +} + +skip :: (it: Iterator($T), count: u32) -> Iterator(T) { + SkipIterator :: struct (T: type_expr) { + iterator: Iterator(T); + to_skip: i32; + skipped: bool = false; + } + + skip_iterator := new(#type SkipIterator(T)); + skip_iterator.iterator = it; + skip_iterator.to_skip = count; + + next :: ($T: type_expr, data: rawptr) -> (T, bool) { + si := cast(^SkipIterator(T)) data; + + while !si.skipped && si.to_skip > 0 { + si.to_skip -= 1; + value, cont := si.iterator.next(si.iterator.data); + + if !cont { + si.skipped = true; + return value, false; + } + } + + return si.iterator.next(si.iterator.data); + } + + close :: ($T: type_expr, data: rawptr) { + si := cast(^SkipIterator(T)) data; + si.iterator.close(si.iterator.data); + cfree(data); + } + + return .{ + data = skip_iterator, + next = #solidify next { T=T }, + close = #solidify close { T=T }, + }; +} + +fold :: (it: Iterator($T), initial_value: R, combine: (T, $R) -> R) -> R { + for value: it { + initial_value = combine(value, initial_value); + } + + return initial_value; +} + +to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T { + array :: package core.array + + arr := array.make(T, allocator=allocator); + for v: it do array.push(^arr, v); + + return arr; +} diff --git a/core/container/list.onyx b/core/container/list.onyx new file mode 100644 index 00000000..d4bd0658 --- /dev/null +++ b/core/container/list.onyx @@ -0,0 +1,106 @@ +package core.list + +ListElem :: struct (T: type_expr) { + next: ^ListElem(T) = null; + prev: ^ListElem(T) = null; + data: T; +} + +List :: struct (T: type_expr) { + allocator: Allocator; + + first: ^ListElem(T) = null; + last: ^ListElem(T) = null; +} + +make :: ($T: type_expr, allocator := context.allocator) -> List(T) { + return .{ allocator = allocator }; +} + +push_end :: (list: ^List($T), x: T) { + new_elem := allocate_elem(list); + new_elem.data = x; + + new_elem.prev = list.last; + list.last.next = new_elem; + list.last = new_elem; + + if list.first == null do list.first = new_elem; +} + +push_begin :: (list: ^List($T), x: T) { + new_elem := allocate_elem(list); + new_elem.data = x; + + new_elem.next = list.first; + list.first.prev = new_elem; + list.first = new_elem; + + if list.last == null do list.last = new_elem; +} + +pop_end :: (list: ^List($T), default: T = 0) -> T { + if list.last == null do return default; + + end := list.last; + list.last = list.last.prev; + list.last.next = null; + + defer raw_free(list.allocator, end); + return end.data; +} + +pop_begin :: (list: ^List($T), default: T = 0) -> T { + if list.last == null do return default; + + begin := list.first; + list.first = list.first.next; + list.first.prev = null; + + defer raw_free(list.allocator, begin); + return begin.data; +} + +contains :: (list: ^List($T), x: T) -> bool { + elem := list.first; + while elem != null { + if elem.data == x do return true; + elem = elem.next; + } + + return false; +} + +get_iterator :: (list: ^List($T)) -> Iterator(T) { + iterator_next :: ($T: type_expr, data: rawptr) -> (T, bool) { + list_iter := cast(^ListIterator(T)) data; + + use package core.intrinsics.onyx { __zero_value } + if list_iter.current == null do return __zero_value(T), false; + + defer list_iter.current = list_iter.current.next; + return list_iter.current.data, true; + } + + iterator_close :: (data: rawptr) { + cfree(data); + } + + ListIterator :: struct (T: type_expr) { + current: ^ListElem(T); + } + + list_iterator := new(#type ListIterator(T)); + list_iterator.current = list.first; + + return .{ + data = list_iterator, + next = #solidify iterator_next { T = T }, + close = iterator_close, + }; +} + +#private_file +allocate_elem :: (list: ^List($T)) -> ^ListElem(T) { + return new(#type ListElem(T), allocator=list.allocator); +} diff --git a/core/container/map.onyx b/core/container/map.onyx new file mode 100644 index 00000000..1153597a --- /dev/null +++ b/core/container/map.onyx @@ -0,0 +1,159 @@ +package core.map + +#private_file array :: package core.array +#private_file string :: package core.string + +Map :: struct (K: type_expr, V: type_expr) { + hashes : [..] i32; + entries : [..] Entry(K, V); + + // The value provided by `map.get`, if nothing was found. + default_value : V; + + Entry :: struct (K: type_expr, V: type_expr) { + next : i32; + key : K; + value : V; + } +} + +make :: ($Key: type_expr, $Value: type_expr, default: Value = 0, hash_count: i32 = 16) -> Map(Key, Value) { + map : Map(Key, Value); + init(^map, default = default, hash_count = hash_count); + return map; +} + +init :: (use map: ^Map($K, $V), default: V = ~~0, hash_count: i32 = 16) { + array.init(^hashes, hash_count); + array.init(^entries, 4); + + default_value = default; + + for i: 0 .. hash_count do array.push(^hashes, -1); +} + +free :: (use map: ^Map($K, $V)) { + array.free(^hashes); + array.free(^entries); +} + +put :: (use map: ^Map($K, $V), key: K, value: V) { + lr := lookup(map, key); + + if lr.entry_index >= 0 { + entries[lr.entry_index].value = value; + return; + } + + entry : Map.Entry(K, V); + entry.key = key; + entry.value = value; + entry.next = hashes[lr.hash_index]; + + array.push(^entries, entry); + + hashes[lr.hash_index] = entries.count - 1; +} + +has :: (use map: ^Map($K, $V), key: K) -> bool { + lr := lookup(map, key); + return lr.entry_index >= 0; +} + +get :: (use map: ^Map($K, $V), key: K) -> V { + lr := lookup(map, key); + if lr.entry_index >= 0 do return entries[lr.entry_index].value; + + return default_value; +} + +get_ptr :: (use map: ^Map($K, $V), key: K) -> ^V { + lr := lookup(map, key); + if lr.entry_index >= 0 do return ^entries[lr.entry_index].value; + + return null; +} + +delete :: (use map: ^Map($K, $V), key: K) { + lr := lookup(map, key); + if lr.entry_index < 0 do return; + + if lr.entry_prev < 0 do hashes[lr.hash_index] = entries[lr.entry_index].next; + else do entries[lr.entry_prev].next = entries[lr.entry_index].next; + + if lr.entry_index == entries.count - 1 { + array.pop(^entries); + return; + } + + array.fast_delete(^entries, lr.entry_index); + last := lookup(map, entries[lr.entry_index].key); + if last.entry_prev >= 0 do entries[last.entry_prev].next = lr.entry_index; + else do hashes[last.hash_index] = lr.entry_index; +} + +update :: (use map: ^Map($K, $V), key: K, f: (^V) -> void) { + lr := lookup(map, key); + if lr.entry_index < 0 do return; + + f(^entries[lr.entry_index].value); +} + +clear :: (use map: ^Map($K, $V)) { + for i: 0 .. hashes.count do hashes.data[i] = -1; + entries.count = 0; +} + +empty :: (use map: ^Map($K, $V)) -> bool { + return entries.count == 0; +} + +hash_function :: proc { + (key: rawptr) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; }, + (key: i32) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; }, + (key: i64) -> u32 { return cast(u32) (cast(u64) 0xcbf29ce7 ^ cast(u64) key); }, + (key: str) -> u32 { + hash: u32 = 5381; + for ch: key do hash += (hash << 5) + ~~ch; + return hash; + }, +} + +// CLEANUP: This could be replaced with operator overloading of '==' +cmp_function :: proc { + (a: rawptr, b: rawptr) -> bool { return a == b; }, + (a: i32, b: i32) -> bool { return a == b; }, + (a: i64, b: i64) -> bool { return a == b; }, + + string.equal, +} + +// +// Private symbols +// + +#private_file +MapLookupResult :: struct { + hash_index : i32 = -1; + entry_index : i32 = -1; + entry_prev : i32 = -1; +} + +#private_file +lookup :: (use map: ^Map($K, $V), key: K) -> MapLookupResult { + lr := MapLookupResult.{}; + + hash: u32 = hash_function(key); // You cannot use this type for the key, unless you add an overload. + + lr.hash_index = hash % hashes.count; + lr.entry_index = hashes[lr.hash_index]; + + while lr.entry_index >= 0 { + if cmp_function(entries[lr.entry_index].key, key) do return lr; + + lr.entry_prev = lr.entry_index; + lr.entry_index = entries[lr.entry_index].next; + } + + return lr; +} diff --git a/core/iter.onyx b/core/iter.onyx deleted file mode 100644 index ff8f0297..00000000 --- a/core/iter.onyx +++ /dev/null @@ -1,194 +0,0 @@ -package core.iter - -use package core.intrinsics.onyx { __zero_value } - -filter :: (it: Iterator($T), predicate: (T) -> bool) -> Iterator(T) { - FilterIterator :: struct (T: type_expr) { - iterator: Iterator(T); - predicate: (T) -> bool; - } - - filter_iterator := new(#type FilterIterator(T)); - filter_iterator.iterator = it; - filter_iterator.predicate = predicate; - - next :: ($T: type_expr, data: rawptr) -> (T, bool) { - fi := cast(^FilterIterator(T)) data; - - value, cont := fi.iterator.next(fi.iterator.data); - if cont { - while !fi.predicate(value) { - value, cont = fi.iterator.next(fi.iterator.data); - if !cont do return value, false; - } - - return value, true; - } else { - return value, false; - } - } - - close :: ($T: type_expr, data: rawptr) { - fi := cast(^FilterIterator(T)) data; - fi.iterator.close(fi.iterator.data); - cfree(data); - } - - return .{ - data = filter_iterator, - next = #solidify next { T=T }, - close = #solidify close { T=T }, - }; -} - -map :: (it: Iterator($T), transform: (T) -> $R) -> Iterator(R) { - MapIterator :: struct (T: type_expr, R: type_expr) { - iterator: Iterator(T); - transform: (T) -> R; - } - - map_iterator := new(#type MapIterator(T, R)); - map_iterator.iterator = it; - map_iterator.transform = transform; - - next :: ($T: type_expr, $R: type_expr, data: rawptr) -> (R, bool) { - mi := cast(^MapIterator(T, R)) data; - - value, cont := mi.iterator.next(mi.iterator.data); - if !cont do return value, false; - - return mi.transform(value), true; - } - - close :: ($T: type_expr, $R: type_expr, data: rawptr) { - mi := cast(^MapIterator(T, R)) data; - mi.iterator.close(mi.iterator.data); - cfree(data); - } - - return .{ - data = map_iterator, - next = #solidify next { T=T, R=R }, - close = #solidify close { T=T, R=R }, - }; -} - -take :: (it: Iterator($T), count: u32) -> Iterator(T) { - TakeIterator :: struct (T: type_expr) { - iterator: Iterator(T); - remaining: u32; - } - - take_iterator := new(#type TakeIterator(T)); - take_iterator.iterator = it; - take_iterator.remaining = count; - - next :: ($T: type_expr, data: rawptr) -> (T, bool) { - ti := cast(^TakeIterator(T)) data; - if ti.remaining == 0 do return __zero_value(T), false; - - ti.remaining -= 1; - return ti.iterator.next(ti.iterator.data); - } - - close :: ($T: type_expr, data: rawptr) { - ti := cast(^TakeIterator(T)) data; - ti.iterator.close(ti.iterator.data); - cfree(data); - } - - return .{ - data = take_iterator, - next = #solidify next { T=T }, - close = #solidify close { T=T }, - }; -} - -take_while :: (it: Iterator($T), predicate: (T) -> bool) -> Iterator(T) { - TakeIterator :: struct (T: type_expr) { - iterator: Iterator(T); - predicate: (T) -> bool; - } - - take_iterator := new(#type TakeIterator(T)); - take_iterator.iterator = it; - take_iterator.predicate = predicate; - - next :: ($T: type_expr, data: rawptr) -> (T, bool) { - ti := cast(^TakeIterator(T)) data; - - value, cont := ti.iterator.next(ti.iterator.data); - if !cont do return value, false; - - return value, ti.predicate(value); - } - - close :: ($T: type_expr, data: rawptr) { - ti := cast(^TakeIterator(T)) data; - ti.iterator.close(ti.iterator.data); - cfree(data); - } - - return .{ - data = take_iterator, - next = #solidify next { T=T }, - close = #solidify close { T=T }, - }; -} - -skip :: (it: Iterator($T), count: u32) -> Iterator(T) { - SkipIterator :: struct (T: type_expr) { - iterator: Iterator(T); - to_skip: i32; - skipped: bool = false; - } - - skip_iterator := new(#type SkipIterator(T)); - skip_iterator.iterator = it; - skip_iterator.to_skip = count; - - next :: ($T: type_expr, data: rawptr) -> (T, bool) { - si := cast(^SkipIterator(T)) data; - - while !si.skipped && si.to_skip > 0 { - si.to_skip -= 1; - value, cont := si.iterator.next(si.iterator.data); - - if !cont { - si.skipped = true; - return value, false; - } - } - - return si.iterator.next(si.iterator.data); - } - - close :: ($T: type_expr, data: rawptr) { - si := cast(^SkipIterator(T)) data; - si.iterator.close(si.iterator.data); - cfree(data); - } - - return .{ - data = skip_iterator, - next = #solidify next { T=T }, - close = #solidify close { T=T }, - }; -} - -fold :: (it: Iterator($T), initial_value: R, combine: (T, $R) -> R) -> R { - for value: it { - initial_value = combine(value, initial_value); - } - - return initial_value; -} - -to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T { - array :: package core.array - - arr := array.make(T, allocator=allocator); - for v: it do array.push(^arr, v); - - return arr; -} diff --git a/core/list.onyx b/core/list.onyx deleted file mode 100644 index d4bd0658..00000000 --- a/core/list.onyx +++ /dev/null @@ -1,106 +0,0 @@ -package core.list - -ListElem :: struct (T: type_expr) { - next: ^ListElem(T) = null; - prev: ^ListElem(T) = null; - data: T; -} - -List :: struct (T: type_expr) { - allocator: Allocator; - - first: ^ListElem(T) = null; - last: ^ListElem(T) = null; -} - -make :: ($T: type_expr, allocator := context.allocator) -> List(T) { - return .{ allocator = allocator }; -} - -push_end :: (list: ^List($T), x: T) { - new_elem := allocate_elem(list); - new_elem.data = x; - - new_elem.prev = list.last; - list.last.next = new_elem; - list.last = new_elem; - - if list.first == null do list.first = new_elem; -} - -push_begin :: (list: ^List($T), x: T) { - new_elem := allocate_elem(list); - new_elem.data = x; - - new_elem.next = list.first; - list.first.prev = new_elem; - list.first = new_elem; - - if list.last == null do list.last = new_elem; -} - -pop_end :: (list: ^List($T), default: T = 0) -> T { - if list.last == null do return default; - - end := list.last; - list.last = list.last.prev; - list.last.next = null; - - defer raw_free(list.allocator, end); - return end.data; -} - -pop_begin :: (list: ^List($T), default: T = 0) -> T { - if list.last == null do return default; - - begin := list.first; - list.first = list.first.next; - list.first.prev = null; - - defer raw_free(list.allocator, begin); - return begin.data; -} - -contains :: (list: ^List($T), x: T) -> bool { - elem := list.first; - while elem != null { - if elem.data == x do return true; - elem = elem.next; - } - - return false; -} - -get_iterator :: (list: ^List($T)) -> Iterator(T) { - iterator_next :: ($T: type_expr, data: rawptr) -> (T, bool) { - list_iter := cast(^ListIterator(T)) data; - - use package core.intrinsics.onyx { __zero_value } - if list_iter.current == null do return __zero_value(T), false; - - defer list_iter.current = list_iter.current.next; - return list_iter.current.data, true; - } - - iterator_close :: (data: rawptr) { - cfree(data); - } - - ListIterator :: struct (T: type_expr) { - current: ^ListElem(T); - } - - list_iterator := new(#type ListIterator(T)); - list_iterator.current = list.first; - - return .{ - data = list_iterator, - next = #solidify iterator_next { T = T }, - close = iterator_close, - }; -} - -#private_file -allocate_elem :: (list: ^List($T)) -> ^ListElem(T) { - return new(#type ListElem(T), allocator=list.allocator); -} diff --git a/core/map.onyx b/core/map.onyx deleted file mode 100644 index 1153597a..00000000 --- a/core/map.onyx +++ /dev/null @@ -1,159 +0,0 @@ -package core.map - -#private_file array :: package core.array -#private_file string :: package core.string - -Map :: struct (K: type_expr, V: type_expr) { - hashes : [..] i32; - entries : [..] Entry(K, V); - - // The value provided by `map.get`, if nothing was found. - default_value : V; - - Entry :: struct (K: type_expr, V: type_expr) { - next : i32; - key : K; - value : V; - } -} - -make :: ($Key: type_expr, $Value: type_expr, default: Value = 0, hash_count: i32 = 16) -> Map(Key, Value) { - map : Map(Key, Value); - init(^map, default = default, hash_count = hash_count); - return map; -} - -init :: (use map: ^Map($K, $V), default: V = ~~0, hash_count: i32 = 16) { - array.init(^hashes, hash_count); - array.init(^entries, 4); - - default_value = default; - - for i: 0 .. hash_count do array.push(^hashes, -1); -} - -free :: (use map: ^Map($K, $V)) { - array.free(^hashes); - array.free(^entries); -} - -put :: (use map: ^Map($K, $V), key: K, value: V) { - lr := lookup(map, key); - - if lr.entry_index >= 0 { - entries[lr.entry_index].value = value; - return; - } - - entry : Map.Entry(K, V); - entry.key = key; - entry.value = value; - entry.next = hashes[lr.hash_index]; - - array.push(^entries, entry); - - hashes[lr.hash_index] = entries.count - 1; -} - -has :: (use map: ^Map($K, $V), key: K) -> bool { - lr := lookup(map, key); - return lr.entry_index >= 0; -} - -get :: (use map: ^Map($K, $V), key: K) -> V { - lr := lookup(map, key); - if lr.entry_index >= 0 do return entries[lr.entry_index].value; - - return default_value; -} - -get_ptr :: (use map: ^Map($K, $V), key: K) -> ^V { - lr := lookup(map, key); - if lr.entry_index >= 0 do return ^entries[lr.entry_index].value; - - return null; -} - -delete :: (use map: ^Map($K, $V), key: K) { - lr := lookup(map, key); - if lr.entry_index < 0 do return; - - if lr.entry_prev < 0 do hashes[lr.hash_index] = entries[lr.entry_index].next; - else do entries[lr.entry_prev].next = entries[lr.entry_index].next; - - if lr.entry_index == entries.count - 1 { - array.pop(^entries); - return; - } - - array.fast_delete(^entries, lr.entry_index); - last := lookup(map, entries[lr.entry_index].key); - if last.entry_prev >= 0 do entries[last.entry_prev].next = lr.entry_index; - else do hashes[last.hash_index] = lr.entry_index; -} - -update :: (use map: ^Map($K, $V), key: K, f: (^V) -> void) { - lr := lookup(map, key); - if lr.entry_index < 0 do return; - - f(^entries[lr.entry_index].value); -} - -clear :: (use map: ^Map($K, $V)) { - for i: 0 .. hashes.count do hashes.data[i] = -1; - entries.count = 0; -} - -empty :: (use map: ^Map($K, $V)) -> bool { - return entries.count == 0; -} - -hash_function :: proc { - (key: rawptr) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; }, - (key: i32) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; }, - (key: i64) -> u32 { return cast(u32) (cast(u64) 0xcbf29ce7 ^ cast(u64) key); }, - (key: str) -> u32 { - hash: u32 = 5381; - for ch: key do hash += (hash << 5) + ~~ch; - return hash; - }, -} - -// CLEANUP: This could be replaced with operator overloading of '==' -cmp_function :: proc { - (a: rawptr, b: rawptr) -> bool { return a == b; }, - (a: i32, b: i32) -> bool { return a == b; }, - (a: i64, b: i64) -> bool { return a == b; }, - - string.equal, -} - -// -// Private symbols -// - -#private_file -MapLookupResult :: struct { - hash_index : i32 = -1; - entry_index : i32 = -1; - entry_prev : i32 = -1; -} - -#private_file -lookup :: (use map: ^Map($K, $V), key: K) -> MapLookupResult { - lr := MapLookupResult.{}; - - hash: u32 = hash_function(key); // You cannot use this type for the key, unless you add an overload. - - lr.hash_index = hash % hashes.count; - lr.entry_index = hashes[lr.hash_index]; - - while lr.entry_index >= 0 { - if cmp_function(entries[lr.entry_index].key, key) do return lr; - - lr.entry_prev = lr.entry_index; - lr.entry_index = entries[lr.entry_index].next; - } - - return lr; -} diff --git a/core/std.onyx b/core/std.onyx index f245a718..677d0e65 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -4,10 +4,10 @@ package core #load "core/alloc" #load "core/memory" -#load "core/array" -#load "core/map" -#load "core/list" -#load "core/iter" +#load "core/container/array" +#load "core/container/map" +#load "core/container/list" +#load "core/container/iter" #load "core/conv" #load "core/math"