mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
@@ -54,7 +54,12 @@ container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: type
|
||||
|
||||
|
||||
when !NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
|
||||
when ODIN_ARCH == .i386 && ODIN_OS == .Windows {
|
||||
// Thread-local storage is problematic on Windows i386
|
||||
global_default_temp_allocator_data: Default_Temp_Allocator
|
||||
} else {
|
||||
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
|
||||
}
|
||||
}
|
||||
|
||||
@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
|
||||
@@ -65,31 +70,33 @@ init_global_temporary_allocator :: proc(size: int, backup_allocator := context.a
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
copy_slice_raw :: proc "contextless" (dst, src: rawptr, dst_len, src_len, elem_size: int) -> int {
|
||||
n := min(dst_len, src_len)
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(dst, src, n*elem_size)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// `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 := min(len(dst), len(src))
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(E))
|
||||
}
|
||||
return n
|
||||
copy_slice :: #force_inline proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
return copy_slice_raw(raw_data(dst), raw_data(src), len(dst), len(src), size_of(E))
|
||||
}
|
||||
|
||||
// `copy_from_string` is a built-in procedure that copies elements from a source string `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_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
|
||||
n := min(len(dst), len(src))
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n)
|
||||
}
|
||||
return n
|
||||
copy_from_string :: #force_inline proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
|
||||
return copy_slice_raw(raw_data(dst), raw_data(src), len(dst), len(src), 1)
|
||||
}
|
||||
|
||||
// `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
|
||||
@@ -98,12 +105,8 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
|
||||
//
|
||||
// Prefer the procedure group `copy`.
|
||||
@builtin
|
||||
copy_from_string16 :: proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int {
|
||||
n := min(len(dst), len(src))
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(u16))
|
||||
}
|
||||
return n
|
||||
copy_from_string16 :: #force_inline proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int {
|
||||
return copy_slice_raw(raw_data(dst), raw_data(src), len(dst), len(src), 2)
|
||||
}
|
||||
|
||||
// `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`.
|
||||
@@ -166,11 +169,17 @@ remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #calle
|
||||
@builtin
|
||||
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
res = array[len(array)-1]
|
||||
(^Raw_Dynamic_Array)(array).len -= 1
|
||||
_pop_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E))
|
||||
return res
|
||||
}
|
||||
|
||||
_pop_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int, loc := #caller_location) {
|
||||
end := rawptr(uintptr(array.data) + uintptr(elem_size*(array.len-1)))
|
||||
intrinsics.mem_copy_non_overlapping(res, end, elem_size)
|
||||
array.len -= 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
// `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.
|
||||
@@ -334,20 +343,19 @@ 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, 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)
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
|
||||
t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
|
||||
return
|
||||
}
|
||||
@(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))
|
||||
t = (^T)(raw_data(mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return))
|
||||
return
|
||||
}
|
||||
|
||||
@(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))
|
||||
t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
|
||||
if t != nil {
|
||||
t^ = data
|
||||
}
|
||||
@@ -357,14 +365,21 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
|
||||
|
||||
@(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_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (res: T, err: Allocator_Error) #optional_allocator_error {
|
||||
err = _make_aligned_type_erased(&res, size_of(E), len, alignment, allocator, loc)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_make_aligned_type_erased :: proc(slice: rawptr, elem_size: int, len: int, alignment: int, allocator: Allocator, loc := #caller_location) -> Allocator_Error {
|
||||
make_slice_error_loc(loc, len)
|
||||
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc)
|
||||
if data == nil && size_of(E) != 0 {
|
||||
return nil, err
|
||||
data, err := mem_alloc_bytes(elem_size*len, alignment, allocator, loc)
|
||||
if data == nil && elem_size != 0 {
|
||||
return err
|
||||
}
|
||||
s := Raw_Slice{raw_data(data), len}
|
||||
return transmute(T)s, err
|
||||
(^Raw_Slice)(slice).data = raw_data(data)
|
||||
(^Raw_Slice)(slice).len = len
|
||||
return err
|
||||
}
|
||||
|
||||
// `make_slice` allocates and initializes a slice. Like `new`, the first argument is a type, not a value.
|
||||
@@ -372,24 +387,27 @@ make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocat
|
||||
//
|
||||
// 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)
|
||||
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (res: T, err: Allocator_Error) #optional_allocator_error {
|
||||
err = _make_aligned_type_erased(&res, size_of(E), len, align_of(E), allocator, loc)
|
||||
return
|
||||
}
|
||||
// `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, 0, allocator, loc)
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), 0, 0, allocator, loc)
|
||||
return
|
||||
}
|
||||
// `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)
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, len, allocator, loc)
|
||||
return
|
||||
}
|
||||
// `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.
|
||||
@@ -495,7 +513,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
// Note: Prefer the procedure group `reserve`
|
||||
@builtin
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, #any_int 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
|
||||
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
|
||||
}
|
||||
|
||||
// Shrinks the capacity of a map down to the current length.
|
||||
@@ -524,7 +542,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
||||
return
|
||||
}
|
||||
|
||||
_append_elem :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
_append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -569,7 +587,7 @@ non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc :
|
||||
}
|
||||
}
|
||||
|
||||
_append_elems :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
_append_elems :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -818,7 +836,7 @@ 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`.
|
||||
_reserve_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
_reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -863,7 +881,7 @@ non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity
|
||||
}
|
||||
|
||||
|
||||
_resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
_resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -989,6 +989,9 @@ __dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
|
||||
// IMPORTANT: USED WITHIN THE COMPILER
|
||||
@(private)
|
||||
__dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
return map_reserve_dynamic(m, info, uintptr(new_capacity), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,19 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
return true
|
||||
}
|
||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||
when ODIN_ARCH == .i386 && !ODIN_NO_CRT {
|
||||
// Windows i386 with CRT: libcmt provides mainCRTStartup which calls _main
|
||||
// Note: "c" calling convention adds underscore prefix automatically on i386
|
||||
@(link_name="main", linkage="strong", require)
|
||||
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
return 0
|
||||
}
|
||||
} else when ODIN_NO_CRT {
|
||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||
mainCRTStartup :: proc "system" () -> i32 {
|
||||
context = default_context()
|
||||
|
||||
@@ -71,10 +71,12 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return
|
||||
|
||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||
if zero_memory && new_size > old_size {
|
||||
new_region := raw_data(new_memory[old_size:])
|
||||
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||
when ODIN_OS != .Windows {
|
||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||
if zero_memory && new_size > old_size {
|
||||
new_region := raw_data(new_memory[old_size:])
|
||||
conditional_mem_zero(new_region, new_size - old_size)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+59
-10
@@ -123,7 +123,7 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
|
||||
|
||||
mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
mem_alloc_bytes :: #force_no_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||
if size == 0 || allocator.procedure == nil{
|
||||
return nil, nil
|
||||
@@ -131,7 +131,7 @@ mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNM
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
mem_alloc :: #force_no_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||
if size == 0 || allocator.procedure == nil {
|
||||
return nil, nil
|
||||
@@ -139,7 +139,7 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
mem_alloc_non_zeroed :: #force_no_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||
if size == 0 || allocator.procedure == nil {
|
||||
return nil, nil
|
||||
@@ -147,7 +147,7 @@ mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_A
|
||||
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
mem_free :: #force_no_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -155,7 +155,7 @@ mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc
|
||||
return err
|
||||
}
|
||||
|
||||
mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
mem_free_with_size :: #force_no_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -163,7 +163,7 @@ mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator
|
||||
return err
|
||||
}
|
||||
|
||||
mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
mem_free_bytes :: #force_no_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -172,14 +172,14 @@ mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocat
|
||||
}
|
||||
|
||||
|
||||
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||
mem_free_all :: #force_no_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||
if allocator.procedure != nil {
|
||||
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
_mem_resize :: #force_no_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
@@ -230,6 +230,55 @@ non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int
|
||||
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
||||
}
|
||||
|
||||
conditional_mem_zero :: proc "contextless" (data: rawptr, n_: int) #no_bounds_check {
|
||||
// When acquiring memory from the OS for the first time it's likely that the
|
||||
// OS already gives the zero page mapped multiple times for the request. The
|
||||
// actual allocation does not have physical pages allocated to it until those
|
||||
// pages are written to which causes a page-fault. This is often called COW
|
||||
// (Copy on Write)
|
||||
//
|
||||
// You do not want to actually zero out memory in this case because it would
|
||||
// cause a bunch of page faults decreasing the speed of allocations and
|
||||
// increase the amount of actual resident physical memory used.
|
||||
//
|
||||
// Instead a better technique is to check if memory is zerored before zeroing
|
||||
// it. This turns out to be an important optimization in practice, saving
|
||||
// nearly half (or more) the amount of physical memory used by an application.
|
||||
// This is why every implementation of calloc in libc does this optimization.
|
||||
//
|
||||
// It may seem counter-intuitive but most allocations in an application are
|
||||
// wasted and never used. When you consider something like a [dynamic]T which
|
||||
// always doubles in capacity on resize but you rarely ever actually use the
|
||||
// full capacity of a dynamic array it means you have a lot of resident waste
|
||||
// if you actually zeroed the remainder of the memory.
|
||||
//
|
||||
// Keep in mind the OS is already guaranteed to give you zeroed memory by
|
||||
// mapping in this zero page multiple times so in the best case there is no
|
||||
// need to actually zero anything. As for testing all this memory for a zero
|
||||
// value, it costs nothing because the the same zero page is used for the
|
||||
// whole allocation and will exist in L1 cache for the entire zero checking
|
||||
// process.
|
||||
|
||||
if n_ <= 0 {
|
||||
return
|
||||
}
|
||||
n := uint(n_)
|
||||
|
||||
n_words := n / size_of(uintptr)
|
||||
p_words := ([^]uintptr)(data)[:n_words]
|
||||
p_bytes := ([^]byte)(data)[size_of(uintptr) * n_words:n]
|
||||
for &p_word in p_words {
|
||||
if p_word != 0 {
|
||||
p_word = 0
|
||||
}
|
||||
}
|
||||
for &p_byte in p_bytes {
|
||||
if p_byte != 0 {
|
||||
p_byte = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
switch {
|
||||
case n == 0: return true
|
||||
@@ -667,7 +716,7 @@ quaternion256_eq :: #force_inline proc "contextless" (a, b: quaternion256) -> bo
|
||||
quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b) }
|
||||
|
||||
|
||||
string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int) {
|
||||
string_decode_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
||||
|
||||
@(static, rodata) accept_sizes := [256]u8{
|
||||
@@ -782,7 +831,7 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
}
|
||||
|
||||
|
||||
string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) {
|
||||
string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) {
|
||||
REPLACEMENT_CHAR :: '\ufffd'
|
||||
_surr1 :: 0xd800
|
||||
_surr2 :: 0xdc00
|
||||
|
||||
Reference in New Issue
Block a user