mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-29 02:40:05 +00:00
Merge branch 'master' into separate-int-word-sizes
This commit is contained in:
+190
-83
@@ -6,7 +6,7 @@ import "core:intrinsics"
|
||||
Maybe :: union($T: typeid) {T}
|
||||
|
||||
|
||||
@builtin
|
||||
@(builtin, require_results)
|
||||
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Field_Type {
|
||||
@@ -27,6 +27,11 @@ init_global_temporary_allocator :: proc(size: int, backup_allocator := context.a
|
||||
}
|
||||
|
||||
|
||||
// `copy_slice` is a built-in procedure that copies elements from a source slice `src` to a destination slice `dst`.
|
||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||
// of len(src) and len(dst).
|
||||
//
|
||||
// Prefer the procedure group `copy`.
|
||||
@builtin
|
||||
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
n := max(0, min(len(dst), len(src)))
|
||||
@@ -35,6 +40,11 @@ copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
}
|
||||
return n
|
||||
}
|
||||
// `copy_from_string` is a built-in procedure that copies elements from a source slice `src` to a destination string `dst`.
|
||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||
// of len(src) and len(dst).
|
||||
//
|
||||
// Prefer the procedure group `copy`.
|
||||
@builtin
|
||||
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
|
||||
n := max(0, min(len(dst), len(src)))
|
||||
@@ -43,11 +53,20 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
|
||||
}
|
||||
return n
|
||||
}
|
||||
// `copy` is a built-in procedure that copies elements from a source slice `src` to a destination slice/string `dst`.
|
||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||
// of len(src) and len(dst).
|
||||
@builtin
|
||||
copy :: proc{copy_slice, copy_from_string}
|
||||
|
||||
|
||||
|
||||
// `unordered_remove` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
// with the old value, and reducing the length of the dynamic array by 1.
|
||||
//
|
||||
// Note: This is an O(1) operation.
|
||||
// Note: If you the elements to remain in their order, use `ordered_remove`.
|
||||
// Note: If the index is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
@@ -57,7 +76,11 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
|
||||
}
|
||||
(^Raw_Dynamic_Array)(array).len -= 1
|
||||
}
|
||||
|
||||
// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If you the elements do not have to remain in their order, prefer `unordered_remove`.
|
||||
// Note: If the index is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
@@ -67,6 +90,10 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_locati
|
||||
(^Raw_Dynamic_Array)(array).len -= 1
|
||||
}
|
||||
|
||||
// `remove_range` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If the range is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) #no_bounds_check {
|
||||
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
|
||||
@@ -80,6 +107,9 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio
|
||||
}
|
||||
|
||||
|
||||
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, "", loc)
|
||||
@@ -89,6 +119,8 @@ pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bou
|
||||
}
|
||||
|
||||
|
||||
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
@@ -99,6 +131,9 @@ pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, "", loc)
|
||||
@@ -110,6 +145,8 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
|
||||
return res
|
||||
}
|
||||
|
||||
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
@@ -124,12 +161,15 @@ pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_
|
||||
}
|
||||
|
||||
|
||||
// `clear` will set the length of a passed dynamic array or map to `0`
|
||||
@builtin
|
||||
clear :: proc{clear_dynamic_array, clear_map}
|
||||
|
||||
// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
@builtin
|
||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
||||
|
||||
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
@builtin
|
||||
resize :: proc{resize_dynamic_array}
|
||||
|
||||
@@ -137,36 +177,56 @@ resize :: proc{resize_dynamic_array}
|
||||
@builtin
|
||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||
|
||||
// `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation.
|
||||
@builtin
|
||||
free :: proc{mem_free}
|
||||
|
||||
// `free_all` will try to free/reset all of the memory of the given `allocator` if the allocator supports this operation.
|
||||
@builtin
|
||||
free_all :: proc{mem_free_all}
|
||||
|
||||
|
||||
|
||||
// `delete_string` will try to free the underlying data of the passed string, with the given `allocator` if the allocator supports this operation.
|
||||
//
|
||||
// Note: Prefer the procedure group `delete`.
|
||||
@builtin
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
// `delete_cstring` will try to free the underlying data of the passed string, with the given `allocator` if the allocator supports this operation.
|
||||
//
|
||||
// Note: Prefer the procedure group `delete`.
|
||||
@builtin
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free((^byte)(str), allocator, loc)
|
||||
}
|
||||
// `delete_dynamic_array` will try to free the underlying data of the passed dynamic array, with the given `allocator` if the allocator supports this operation.
|
||||
//
|
||||
// Note: Prefer the procedure group `delete`.
|
||||
@builtin
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
// `delete_slice` will try to free the underlying data of the passed sliced, with the given `allocator` if the allocator supports this operation.
|
||||
//
|
||||
// Note: Prefer the procedure group `delete`.
|
||||
@builtin
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
// `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation.
|
||||
//
|
||||
// Note: Prefer the procedure group `delete`.
|
||||
@builtin
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
// `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation.
|
||||
//
|
||||
// Note: Prefer `delete` over the specific `delete_*` procedures where possible.
|
||||
@builtin
|
||||
delete :: proc{
|
||||
delete_string,
|
||||
@@ -179,17 +239,18 @@ delete :: proc{
|
||||
|
||||
// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
|
||||
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
|
||||
@builtin
|
||||
@(builtin, require_results)
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_allocator_error {
|
||||
return new_aligned(T, align_of(T), allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
|
||||
data := mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return
|
||||
t = (^T)(raw_data(data))
|
||||
return
|
||||
}
|
||||
|
||||
@builtin
|
||||
@(builtin, require_results)
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
|
||||
t_data := mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return
|
||||
t = (^T)(raw_data(t_data))
|
||||
@@ -201,6 +262,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16
|
||||
|
||||
@(require_results)
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
make_slice_error_loc(loc, len)
|
||||
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc)
|
||||
@@ -211,19 +273,35 @@ make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocat
|
||||
return transmute(T)s, err
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
// `make_slice` allocates and initializes a slice. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
}
|
||||
@(builtin)
|
||||
// `make_dynamic_array` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
||||
}
|
||||
@(builtin)
|
||||
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
}
|
||||
@(builtin)
|
||||
// `make_dynamic_array_len_cap` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_dynamic_array_error_loc(loc, len, cap)
|
||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
||||
@@ -234,7 +312,11 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
array = transmute(T)s
|
||||
return
|
||||
}
|
||||
@(builtin)
|
||||
// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_map_expr_error_loc(loc, capacity)
|
||||
context.allocator = allocator
|
||||
@@ -242,7 +324,13 @@ make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_
|
||||
err = reserve_map(&m, capacity, loc)
|
||||
return
|
||||
}
|
||||
@(builtin)
|
||||
// `make_multi_pointer` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// This is "similar" to doing `raw_data(make([]E, len, allocator))`.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_slice_error_loc(loc, len)
|
||||
data := mem_alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
|
||||
@@ -254,8 +342,9 @@ make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := con
|
||||
}
|
||||
|
||||
|
||||
// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
|
||||
// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||
// `make` built-in procedure allocates and initializes a value of type slice, dynamic array, map, or multi-pointer (only).
|
||||
//
|
||||
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||
// type of its argument, not a pointer to it.
|
||||
// Make uses the specified allocator, default is context.allocator, default is context.allocator
|
||||
@builtin
|
||||
@@ -270,6 +359,9 @@ make :: proc{
|
||||
|
||||
|
||||
|
||||
// `clear_map` will set the length of a passed map to `0`
|
||||
//
|
||||
// Note: Prefer the procedure group `clear`
|
||||
@builtin
|
||||
clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
if m == nil {
|
||||
@@ -278,19 +370,21 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
map_clear_dynamic((^Raw_Map)(m), map_info(T))
|
||||
}
|
||||
|
||||
// `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `reserve`
|
||||
@builtin
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a map down to the current length.
|
||||
*/
|
||||
// Shrinks the capacity of a map down to the current length.
|
||||
//
|
||||
// Note: Prefer the procedure group `shrink`
|
||||
@builtin
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||
if m != nil {
|
||||
err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
|
||||
did_shrink = err == nil
|
||||
return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -313,18 +407,18 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
||||
|
||||
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0
|
||||
return 0, nil
|
||||
}
|
||||
when size_of(E) == 0 {
|
||||
array := (^Raw_Dynamic_Array)(array)
|
||||
array.len += 1
|
||||
return 1
|
||||
return 1, nil
|
||||
} else {
|
||||
if cap(array) < len(array)+1 {
|
||||
cap := 2 * cap(array) + max(8, 1)
|
||||
_ = reserve(array, cap, loc)
|
||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
||||
}
|
||||
if cap(array)-len(array) > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
@@ -334,31 +428,31 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
||||
data[a.len] = arg
|
||||
}
|
||||
a.len += 1
|
||||
return 1
|
||||
return 1, err
|
||||
}
|
||||
return 0
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
arg_len := len(args)
|
||||
if arg_len <= 0 {
|
||||
return 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
when size_of(E) == 0 {
|
||||
array := (^Raw_Dynamic_Array)(array)
|
||||
array.len += arg_len
|
||||
return arg_len
|
||||
return arg_len, nil
|
||||
} else {
|
||||
if cap(array) < len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
_ = reserve(array, cap, loc)
|
||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
||||
}
|
||||
arg_len = min(cap(array)-len(array), arg_len)
|
||||
if arg_len > 0 {
|
||||
@@ -370,13 +464,13 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
||||
}
|
||||
a.len += arg_len
|
||||
}
|
||||
return arg_len
|
||||
return arg_len, err
|
||||
}
|
||||
}
|
||||
|
||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||
@builtin
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
args := transmute([]E)arg
|
||||
return append_elems(array=array, args=args, loc=loc)
|
||||
}
|
||||
@@ -384,9 +478,14 @@ append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #ca
|
||||
|
||||
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||
@builtin
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
n_arg: int
|
||||
for arg in args {
|
||||
n += append(array = array, args = transmute([]E)(arg), loc = loc)
|
||||
n_arg, err = append(array = array, args = transmute([]E)(arg), loc = loc)
|
||||
n += n_arg
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -396,18 +495,18 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
|
||||
|
||||
@builtin
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0
|
||||
return 0, nil
|
||||
}
|
||||
prev_len := len(array)
|
||||
resize(array, len(array)+1, loc)
|
||||
return len(array)-prev_len
|
||||
resize(array, len(array)+1, loc) or_return
|
||||
return len(array)-prev_len, nil
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -415,18 +514,17 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
m :: 1
|
||||
new_size := n + m
|
||||
|
||||
if resize(array, new_size, loc) {
|
||||
when size_of(E) != 0 {
|
||||
copy(array[index + m:], array[index:])
|
||||
array[index] = arg
|
||||
}
|
||||
ok = true
|
||||
resize(array, new_size, loc) or_return
|
||||
when size_of(E) != 0 {
|
||||
copy(array[index + m:], array[index:])
|
||||
array[index] = arg
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@builtin
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -439,18 +537,17 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
|
||||
m := len(args)
|
||||
new_size := n + m
|
||||
|
||||
if resize(array, new_size, loc) {
|
||||
when size_of(E) != 0 {
|
||||
copy(array[index + m:], array[index:])
|
||||
copy(array[index:], args)
|
||||
}
|
||||
ok = true
|
||||
resize(array, new_size, loc) or_return
|
||||
when size_of(E) != 0 {
|
||||
copy(array[index + m:], array[index:])
|
||||
copy(array[index:], args)
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@builtin
|
||||
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -463,11 +560,10 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
|
||||
m := len(arg)
|
||||
new_size := n + m
|
||||
|
||||
if resize(array, new_size, loc) {
|
||||
copy(array[index+m:], array[index:])
|
||||
copy(array[index:], arg)
|
||||
ok = true
|
||||
}
|
||||
resize(array, new_size, loc) or_return
|
||||
copy(array[index+m:], array[index:])
|
||||
copy(array[index:], arg)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -476,11 +572,12 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
|
||||
|
||||
|
||||
@builtin
|
||||
assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if index < len(array) {
|
||||
array[index] = arg
|
||||
ok = true
|
||||
} else if resize(array, index+1, loc) {
|
||||
} else {
|
||||
resize(array, index+1, loc) or_return
|
||||
array[index] = arg
|
||||
ok = true
|
||||
}
|
||||
@@ -489,11 +586,12 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
|
||||
|
||||
@builtin
|
||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if index+len(args) < len(array) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
} else if resize(array, index+1+len(args), loc) {
|
||||
} else {
|
||||
resize(array, index+1+len(args), loc) or_return
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
}
|
||||
@@ -502,13 +600,14 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
|
||||
|
||||
|
||||
@builtin
|
||||
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if len(args) == 0 {
|
||||
ok = true
|
||||
} else if index+len(args) < len(array) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
} else if resize(array, index+1+len(args), loc) {
|
||||
} else {
|
||||
resize(array, index+1+len(args), loc) or_return
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
}
|
||||
@@ -520,6 +619,9 @@ assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
|
||||
|
||||
|
||||
|
||||
// `clear_dynamic_array` will set the length of a passed dynamic array to `0`
|
||||
//
|
||||
// Note: Prefer the procedure group `clear`.
|
||||
@builtin
|
||||
clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
||||
if array != nil {
|
||||
@@ -527,15 +629,18 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
||||
}
|
||||
}
|
||||
|
||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `reserve`.
|
||||
@builtin
|
||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
|
||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
if capacity <= a.cap {
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.allocator.procedure == nil {
|
||||
@@ -547,26 +652,29 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
new_size := capacity * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
if new_data == nil && new_size > 0 {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
a.data = raw_data(new_data)
|
||||
a.cap = capacity
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `resize`
|
||||
@builtin
|
||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
|
||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
if length <= a.cap {
|
||||
a.len = max(length, 0)
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.allocator.procedure == nil {
|
||||
@@ -578,15 +686,15 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
new_size := length * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
if new_data == nil && new_size > 0 {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
a.data = raw_data(new_data)
|
||||
a.len = length
|
||||
a.cap = length
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -597,8 +705,10 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
Returns false if `cap(array) < new_cap`, or the allocator report failure.
|
||||
|
||||
If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
||||
|
||||
Note: Prefer the procedure group `shrink`
|
||||
*/
|
||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
|
||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -618,15 +728,12 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := new_cap * size_of(E)
|
||||
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc) or_return
|
||||
|
||||
a.data = raw_data(new_data)
|
||||
a.len = min(new_cap, a.len)
|
||||
a.cap = new_cap
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
|
||||
@@ -37,12 +37,12 @@ inverse :: proc{
|
||||
matrix4x4_inverse,
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
|
||||
return conj(transpose(m))
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix_trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
for i in 0..<N {
|
||||
trace += m[i, i]
|
||||
@@ -50,7 +50,7 @@ matrix_trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, row, column: int) -> (minor: T) where N > 1 {
|
||||
K :: N-1
|
||||
cut_down: matrix[K, K]T
|
||||
@@ -66,23 +66,23 @@ matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, row, column: int) ->
|
||||
|
||||
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) {
|
||||
return m[0, 0]
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
return a + b + c
|
||||
}
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
a := adjugate(m)
|
||||
#no_bounds_check for i in 0..<4 {
|
||||
@@ -94,13 +94,13 @@ matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
|
||||
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y = x
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
y[0, 0] = +x[1, 1]
|
||||
y[0, 1] = -x[1, 0]
|
||||
@@ -109,7 +109,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
@@ -124,7 +124,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
@@ -135,13 +135,13 @@ matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
@@ -159,7 +159,7 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d := determinant(x)
|
||||
@@ -180,7 +180,7 @@ matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d: T
|
||||
@@ -204,13 +204,13 @@ matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y:
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
@@ -228,7 +228,7 @@ matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d := determinant(x)
|
||||
@@ -249,7 +249,7 @@ matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
@(builtin, require_results)
|
||||
matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d: T
|
||||
|
||||
@@ -50,6 +50,7 @@ Raw_SOA_Footer_Dynamic_Array :: struct {
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
@(builtin, require_results)
|
||||
raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
|
||||
if array == nil {
|
||||
return nil
|
||||
@@ -58,6 +59,7 @@ raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Sl
|
||||
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
|
||||
return
|
||||
}
|
||||
@(builtin, require_results)
|
||||
raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
|
||||
if array == nil {
|
||||
return nil
|
||||
@@ -78,7 +80,7 @@ raw_soa_footer :: proc{
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
@(builtin, require_results)
|
||||
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
if length <= 0 {
|
||||
return
|
||||
@@ -137,32 +139,31 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
||||
return
|
||||
}
|
||||
|
||||
@builtin
|
||||
@(builtin, require_results)
|
||||
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
return make_soa_aligned(T, length, align_of(E), allocator, loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T) {
|
||||
@(builtin, require_results)
|
||||
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
context.allocator = allocator
|
||||
reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc)
|
||||
return
|
||||
reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
|
||||
return array, nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
|
||||
@(builtin, require_results)
|
||||
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
context.allocator = allocator
|
||||
resize_soa(&array, length, loc)
|
||||
return
|
||||
resize_soa(&array, length, loc) or_return
|
||||
return array, nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, #any_int length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
|
||||
@(builtin, require_results)
|
||||
make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, #any_int length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
context.allocator = allocator
|
||||
if reserve_soa(&array, capacity, loc) {
|
||||
resize_soa(&array, length, loc)
|
||||
}
|
||||
return
|
||||
reserve_soa(&array, capacity, loc) or_return
|
||||
resize_soa(&array, length, loc) or_return
|
||||
return array, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -176,27 +177,25 @@ make_soa :: proc{
|
||||
|
||||
|
||||
@builtin
|
||||
resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> bool {
|
||||
resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
if !reserve_soa(array, length, loc) {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
reserve_soa(array, length, loc) or_return
|
||||
footer := raw_soa_footer(array)
|
||||
footer.len = length
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
|
||||
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
old_cap := cap(array)
|
||||
if capacity <= old_cap {
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
if array.allocator.procedure == nil {
|
||||
@@ -207,7 +206,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
||||
footer := raw_soa_footer(array)
|
||||
if size_of(E) == 0 {
|
||||
footer.cap = capacity
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
ti := type_info_of(typeid_of(T))
|
||||
@@ -238,13 +237,10 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
||||
|
||||
old_data := (^rawptr)(array)^
|
||||
|
||||
new_bytes, err := array.allocator.procedure(
|
||||
new_bytes := array.allocator.procedure(
|
||||
array.allocator.data, .Alloc, new_size, max_align,
|
||||
nil, old_size, loc,
|
||||
)
|
||||
if new_bytes == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
) or_return
|
||||
new_data := raw_data(new_bytes)
|
||||
|
||||
|
||||
@@ -269,31 +265,28 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
||||
new_offset += type.size * capacity
|
||||
}
|
||||
|
||||
_, err = array.allocator.procedure(
|
||||
array.allocator.procedure(
|
||||
array.allocator.data, .Free, 0, max_align,
|
||||
old_data, old_size, loc,
|
||||
)
|
||||
) or_return
|
||||
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
|
||||
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
arg_len := 1
|
||||
|
||||
if cap(array) <= len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
_ = reserve_soa(array, cap, loc)
|
||||
if cap(array) <= len(array) + 1 {
|
||||
cap := 2 * cap(array) + 8
|
||||
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
||||
}
|
||||
arg_len = min(cap(array)-len(array), arg_len)
|
||||
|
||||
footer := raw_soa_footer(array)
|
||||
|
||||
if size_of(E) > 0 && arg_len > 0 {
|
||||
if size_of(E) > 0 && cap(array)-len(array) > 0 {
|
||||
ti := type_info_of(typeid_of(T))
|
||||
ti = type_info_base(ti)
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
@@ -326,12 +319,14 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
||||
soa_offset += type.size * cap(array)
|
||||
item_offset += type.size
|
||||
}
|
||||
footer.len += 1
|
||||
return 1, err
|
||||
}
|
||||
footer.len += arg_len
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
|
||||
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -343,7 +338,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
||||
|
||||
if cap(array) <= len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
_ = reserve_soa(array, cap, loc)
|
||||
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
||||
}
|
||||
arg_len = min(cap(array)-len(array), arg_len)
|
||||
|
||||
@@ -380,8 +375,8 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
||||
item_offset += type.size
|
||||
}
|
||||
}
|
||||
|
||||
footer.len += arg_len
|
||||
return arg_len, err
|
||||
}
|
||||
|
||||
|
||||
@@ -393,21 +388,23 @@ append_soa :: proc{
|
||||
}
|
||||
|
||||
|
||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
when intrinsics.type_struct_field_count(E) != 0 {
|
||||
array := array
|
||||
ptr := (^rawptr)(&array)^
|
||||
free(ptr, allocator, loc)
|
||||
free(ptr, allocator, loc) or_return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) {
|
||||
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
when intrinsics.type_struct_field_count(E) != 0 {
|
||||
array := array
|
||||
ptr := (^rawptr)(&array)^
|
||||
footer := raw_soa_footer(&array)
|
||||
free(ptr, footer.allocator, loc)
|
||||
free(ptr, footer.allocator, loc) or_return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -251,6 +251,26 @@ map_hash_is_valid :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
|
||||
return (hash != 0) & (hash & TOMBSTONE_MASK == 0)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
map_seed :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
|
||||
return map_seed_from_map_data(map_data(m))
|
||||
}
|
||||
|
||||
// splitmix for uintptr
|
||||
@(require_results)
|
||||
map_seed_from_map_data :: #force_inline proc "contextless" (data: uintptr) -> uintptr {
|
||||
when size_of(uintptr) == size_of(u64) {
|
||||
mix := data + 0x9e3779b97f4a7c15
|
||||
mix = (mix ~ (mix >> 30)) * 0xbf58476d1ce4e5b9
|
||||
mix = (mix ~ (mix >> 27)) * 0x94d049bb133111eb
|
||||
return mix ~ (mix >> 31)
|
||||
} else {
|
||||
mix := data + 0x9e3779b9
|
||||
mix = (mix ~ (mix >> 16)) * 0x21f0aaad
|
||||
mix = (mix ~ (mix >> 15)) * 0x735a2d97
|
||||
return mix ~ (mix >> 15)
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the desired position in the array. This is just index % capacity,
|
||||
// but a procedure as there's some math involved here to recover the capacity.
|
||||
@@ -394,32 +414,71 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
||||
tk := map_cell_index_dynamic(sk, info.ks, 1)
|
||||
tv := map_cell_index_dynamic(sv, info.vs, 1)
|
||||
|
||||
|
||||
for {
|
||||
hp := &hs[pos]
|
||||
element_hash := hp^
|
||||
|
||||
if map_hash_is_empty(element_hash) {
|
||||
k_dst := map_cell_index_dynamic(ks, info.ks, pos)
|
||||
v_dst := map_cell_index_dynamic(vs, info.vs, pos)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
|
||||
kp := map_cell_index_dynamic(ks, info.ks, pos)
|
||||
vp := map_cell_index_dynamic(vs, info.vs, pos)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
|
||||
hp^ = h
|
||||
|
||||
return result if result != 0 else v_dst
|
||||
return result if result != 0 else vp
|
||||
}
|
||||
|
||||
if map_hash_is_deleted(element_hash) {
|
||||
next_pos := (pos + 1) & mask
|
||||
|
||||
// backward shift
|
||||
for !map_hash_is_empty(hs[next_pos]) {
|
||||
probe_distance := map_probe_distance(m^, hs[next_pos], next_pos)
|
||||
if probe_distance == 0 {
|
||||
break
|
||||
}
|
||||
probe_distance -= 1
|
||||
|
||||
kp := map_cell_index_dynamic(ks, info.ks, pos)
|
||||
vp := map_cell_index_dynamic(vs, info.vs, pos)
|
||||
kn := map_cell_index_dynamic(ks, info.ks, next_pos)
|
||||
vn := map_cell_index_dynamic(vs, info.vs, next_pos)
|
||||
|
||||
if distance > probe_distance {
|
||||
if result == 0 {
|
||||
result = vp
|
||||
}
|
||||
// move stored into pos; store next
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
|
||||
hs[pos] = h
|
||||
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kn), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(vn), size_of_v)
|
||||
h = hs[next_pos]
|
||||
} else {
|
||||
// move next back 1
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(kn), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(vn), size_of_v)
|
||||
hs[pos] = hs[next_pos]
|
||||
distance = probe_distance
|
||||
}
|
||||
hs[next_pos] = 0
|
||||
pos = (pos + 1) & mask
|
||||
next_pos = (next_pos + 1) & mask
|
||||
distance += 1
|
||||
}
|
||||
|
||||
kp := map_cell_index_dynamic(ks, info.ks, pos)
|
||||
vp := map_cell_index_dynamic(vs, info.vs, pos)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
|
||||
hs[pos] = h
|
||||
|
||||
return result if result != 0 else vp
|
||||
}
|
||||
|
||||
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
|
||||
if map_hash_is_deleted(element_hash) {
|
||||
k_dst := map_cell_index_dynamic(ks, info.ks, pos)
|
||||
v_dst := map_cell_index_dynamic(vs, info.vs, pos)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
|
||||
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
|
||||
hp^ = h
|
||||
|
||||
return result if result != 0 else v_dst
|
||||
}
|
||||
|
||||
if result == 0 {
|
||||
result = map_cell_index_dynamic(vs, info.vs, pos)
|
||||
}
|
||||
@@ -503,6 +562,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
|
||||
}
|
||||
k := map_cell_index_dynamic(ks, info.ks, i)
|
||||
v := map_cell_index_dynamic(vs, info.vs, i)
|
||||
hash = info.key_hasher(rawptr(k), map_seed(resized))
|
||||
_ = map_insert_hash_dynamic(&resized, info, hash, k, v)
|
||||
// Only need to do this comparison on each actually added pair, so do not
|
||||
// fold it into the for loop comparator as a micro-optimization.
|
||||
@@ -550,6 +610,7 @@ map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_I
|
||||
|
||||
k := map_cell_index_dynamic(ks, info.ks, i)
|
||||
v := map_cell_index_dynamic(vs, info.vs, i)
|
||||
hash = info.key_hasher(rawptr(k), map_seed(shrunk))
|
||||
_ = map_insert_hash_dynamic(&shrunk, info, hash, k, v)
|
||||
// Only need to do this comparison on each actually added pair, so do not
|
||||
// fold it into the for loop comparator as a micro-optimization.
|
||||
@@ -581,7 +642,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
||||
if map_len(m) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
h := info.key_hasher(rawptr(k), 0)
|
||||
h := info.key_hasher(rawptr(k), map_seed(m))
|
||||
p := map_desired_position(m, h)
|
||||
d := uintptr(0)
|
||||
c := (uintptr(1) << map_log2_cap(m)) - 1
|
||||
@@ -604,7 +665,7 @@ map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
||||
if map_len(m) == 0 {
|
||||
return false
|
||||
}
|
||||
h := info.key_hasher(rawptr(k), 0)
|
||||
h := info.key_hasher(rawptr(k), map_seed(m))
|
||||
p := map_desired_position(m, h)
|
||||
d := uintptr(0)
|
||||
c := (uintptr(1) << map_log2_cap(m)) - 1
|
||||
@@ -637,7 +698,6 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
|
||||
|
||||
{ // coalesce tombstones
|
||||
// HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots
|
||||
// TODO(bill): we should do backward shift deletion and not rely on tombstone slots
|
||||
mask := (uintptr(1)<<map_log2_cap(m^)) - 1
|
||||
curr_index := uintptr(index)
|
||||
|
||||
@@ -711,7 +771,7 @@ map_get :: proc "contextless" (m: $T/map[$K]$V, key: K) -> (stored_key: K, store
|
||||
info := intrinsics.type_map_info(T)
|
||||
key := key
|
||||
|
||||
h := info.key_hasher(&key, 0)
|
||||
h := info.key_hasher(&key, map_seed(rm))
|
||||
pos := map_desired_position(rm, h)
|
||||
distance := uintptr(0)
|
||||
mask := (uintptr(1) << map_log2_cap(rm)) - 1
|
||||
@@ -762,15 +822,15 @@ __dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info:
|
||||
}
|
||||
|
||||
// IMPORTANT: USED WITHIN THE COMPILER
|
||||
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
||||
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
|
||||
if m.len >= map_resize_threshold(m^) {
|
||||
return map_grow_dynamic(m, info, loc)
|
||||
return map_grow_dynamic(m, info, loc), true
|
||||
}
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
__dynamic_map_set_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> rawptr {
|
||||
return __dynamic_map_set(m, info, info.key_hasher(key, 0), key, value, loc)
|
||||
return __dynamic_map_set(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -781,9 +841,14 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
|
||||
return found
|
||||
}
|
||||
|
||||
if __dynamic_map_check_grow(m, info, loc) != nil {
|
||||
hash := hash
|
||||
err, has_grown := __dynamic_map_check_grow(m, info, loc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if has_grown {
|
||||
hash = info.key_hasher(key, map_seed(m^))
|
||||
}
|
||||
|
||||
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
||||
m.len += 1
|
||||
|
||||
@@ -500,6 +500,42 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
|
||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4
|
||||
}
|
||||
|
||||
string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
RUNE_ERROR :: '\ufffd'
|
||||
RUNE_SELF :: 0x80
|
||||
UTF_MAX :: 4
|
||||
|
||||
r: rune
|
||||
size: int
|
||||
start, end, limit: int
|
||||
|
||||
end = len(s)
|
||||
if end == 0 {
|
||||
return RUNE_ERROR, 0
|
||||
}
|
||||
start = end-1
|
||||
r = rune(s[start])
|
||||
if r < RUNE_SELF {
|
||||
return r, 1
|
||||
}
|
||||
|
||||
limit = max(end - UTF_MAX, 0)
|
||||
|
||||
for start-=1; start >= limit; start-=1 {
|
||||
if (s[start] & 0xc0) != RUNE_SELF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
start = max(start, 0)
|
||||
r, size = string_decode_rune(s[start:end])
|
||||
if start+size != end {
|
||||
return RUNE_ERROR, 1
|
||||
}
|
||||
return r, size
|
||||
}
|
||||
|
||||
|
||||
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
|
||||
return -x if x < 0 else x
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ foreign import "odin_env"
|
||||
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
foreign odin_env {
|
||||
write :: proc "c" (fd: u32, p: []byte) ---
|
||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||
}
|
||||
write(1, data)
|
||||
return len(data), 0
|
||||
|
||||
@@ -8,7 +8,7 @@ ti_int :: struct #raw_union {
|
||||
}
|
||||
|
||||
@(link_name="__ashlti3", linkage="strong")
|
||||
__ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
|
||||
__ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
|
||||
bits_in_dword :: size_of(u32)*8
|
||||
b := u32(b_)
|
||||
|
||||
@@ -29,7 +29,7 @@ __ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
|
||||
|
||||
|
||||
@(link_name="__multi3", linkage="strong")
|
||||
__multi3 :: proc "c" (a, b: i128) -> i128 {
|
||||
__multi3 :: proc "contextless" (a, b: i128) -> i128 {
|
||||
x, y, r: ti_int
|
||||
|
||||
x.all = a
|
||||
|
||||
Reference in New Issue
Block a user