Merge branch 'master' into vendor/curl

This commit is contained in:
gingerBill
2025-10-30 10:54:26 +00:00
355 changed files with 12167 additions and 4562 deletions

View File

@@ -124,13 +124,13 @@ jobs:
build_macos:
name: MacOS Build
if: github.repository == 'odin-lang/Odin'
runs-on: macos-13
runs-on: macos-14 # Intel machine
steps:
- uses: actions/checkout@v4
- name: Download LLVM and setup PATH
run: |
brew update
brew install llvm@20 dylibbundler lld
brew install llvm@20 dylibbundler lld@20
- name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
@@ -169,7 +169,7 @@ jobs:
- name: Download LLVM and setup PATH
run: |
brew update
brew install llvm@20 dylibbundler lld
brew install llvm@20 dylibbundler lld@20
- name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to

6
.gitignore vendored
View File

@@ -302,3 +302,9 @@ misc/featuregen/featuregen
.cache/
.clangd
compile_commands.json
# Dev cmake helpers
build/
cmake-build*/
CMakeLists.txt
sandbox/

View File

@@ -138,10 +138,13 @@ type_is_rune :: proc($T: typeid) -> bool ---
type_is_float :: proc($T: typeid) -> bool ---
type_is_complex :: proc($T: typeid) -> bool ---
type_is_quaternion :: proc($T: typeid) -> bool ---
type_is_string :: proc($T: typeid) -> bool ---
type_is_typeid :: proc($T: typeid) -> bool ---
type_is_any :: proc($T: typeid) -> bool ---
type_is_string :: proc($T: typeid) -> bool ---
type_is_string16 :: proc($T: typeid) -> bool ---
type_is_cstring :: proc($T: typeid) -> bool ---
type_is_cstring16 :: proc($T: typeid) -> bool ---
type_is_endian_platform :: proc($T: typeid) -> bool ---
type_is_endian_little :: proc($T: typeid) -> bool ---
@@ -154,6 +157,7 @@ type_is_indexable :: proc($T: typeid) -> bool ---
type_is_sliceable :: proc($T: typeid) -> bool ---
type_is_comparable :: proc($T: typeid) -> bool ---
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
type_is_nearly_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (including floats)
type_is_dereferenceable :: proc($T: typeid) -> bool ---
type_is_valid_map_key :: proc($T: typeid) -> bool ---
type_is_valid_matrix_elements :: proc($T: typeid) -> bool ---
@@ -240,6 +244,11 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---
constant_floor :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
constant_trunc :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
constant_ceil :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
constant_round :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
// SIMD related
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
@@ -344,6 +353,9 @@ simd_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
// Utility Calls
concatentate :: proc(x, y: $T, z: ..T) -> T where type_is_array(T) || type_is_slice(T) ---
// Returns the value of the procedure where `x` must be a call expression
procedure_of :: proc(x: $T) -> T where type_is_proc(T) ---
@@ -374,10 +386,11 @@ objc_selector :: struct{}
objc_class :: struct{}
objc_ivar :: struct{}
objc_id :: ^objc_object
objc_SEL :: ^objc_selector
objc_Class :: ^objc_class
objc_Ivar :: ^objc_ivar
objc_id :: ^objc_object
objc_SEL :: ^objc_selector
objc_Class :: ^objc_class
objc_Ivar :: ^objc_ivar
objc_instancetype :: distinct objc_id
objc_find_selector :: proc($name: string) -> objc_SEL ---
objc_register_selector :: proc($name: string) -> objc_SEL ---
@@ -385,6 +398,7 @@ objc_find_class :: proc($name: string) -> objc_Class ---
objc_register_class :: proc($name: string) -> objc_Class ---
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) ---
objc_super :: proc(obj: ^$T) -> ^$U where type_is_subtype_of(T, objc_object) && type_is_subtype_of(U, objc_object) ---
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---

View File

@@ -15,7 +15,7 @@
//
// IMPORTANT NOTE(bill): `type_info_of` cannot be used within a
// #shared_global_scope due to the internals of the compiler.
// This could change at a later date if the all these data structures are
// This could change at a later date if all these data structures are
// implemented within the compiler rather than in this "preload" file
//
#+no-instrumentation
@@ -636,6 +636,8 @@ _cleanup_runtime_contextless :: proc "contextless" () {
/////////////////////////////
// type_info_base returns the base-type of a `^Type_Info` stripping the `distinct`ness from the first level
@(require_results)
type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
if info == nil {
return nil
@@ -652,6 +654,10 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
}
// type_info_core returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
// returns the backing integer type of an enum or bit_set `^Type_Info`.
// This is also aliased as `type_info_base_without_enum`
@(require_results)
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
if info == nil {
return nil
@@ -668,6 +674,10 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
}
return base
}
// type_info_base_without_enum returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
// returns the backing integer type of an enum or bit_set `^Type_Info`.
// This is also aliased as `type_info_core`
type_info_base_without_enum :: type_info_core
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
@@ -684,15 +694,23 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
}
when !ODIN_NO_RTTI {
// typeid_base returns the base-type of a `typeid` stripping the `distinct`ness from the first level
typeid_base :: proc "contextless" (id: typeid) -> typeid {
ti := type_info_of(id)
ti = type_info_base(ti)
return ti.id
}
// typeid_core returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
// returns the backing integer type of an enum or bit_set `typeid`.
// This is also aliased as `typeid_base_without_enum`
typeid_core :: proc "contextless" (id: typeid) -> typeid {
ti := type_info_core(type_info_of(id))
return ti.id
}
// typeid_base_without_enum returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
// returns the backing integer type of an enum or bit_set `typeid`.
// This is also aliased as `typeid_core`
typeid_base_without_enum :: typeid_core
}
@@ -708,11 +726,15 @@ default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, opt
// Nothing
}
// Returns the default logger used by `context.logger`
@(require_results)
default_logger :: proc() -> Logger {
return Logger{default_logger_proc, nil, Logger_Level.Debug, nil}
}
// Returns the default `context`
@(require_results)
default_context :: proc "contextless" () -> Context {
c: Context
__init_context(&c)

View File

@@ -54,9 +54,16 @@ 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
}
}
// Initializes the global temporary allocator used as the default `context.temp_allocator`.
// This is ignored when `NO_DEFAULT_TEMP_ALLOCATOR` is true.
@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
when !NO_DEFAULT_TEMP_ALLOCATOR {
@@ -65,31 +72,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 +107,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 +171,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 +345,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 +367,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 +389,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.
@@ -494,7 +514,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.
@@ -523,7 +543,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
}
@@ -546,6 +566,7 @@ _append_elem :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, alig
return
}
// `append_elem` appends an element to the end of a dynamic array.
@builtin
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
when size_of(E) == 0 {
@@ -557,6 +578,9 @@ append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller
}
}
// `non_zero_append_elem` appends an element to the end of a dynamic array, without zeroing any reserved memory
//
// Note: Prefer using the procedure group `non_zero_append
@builtin
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
when size_of(E) == 0 {
@@ -568,7 +592,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
}
@@ -595,6 +619,9 @@ _append_elems :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
return arg_len, err
}
// `append_elems` appends `args` to the end of a dynamic array.
//
// Note: Prefer using the procedure group `append`.
@builtin
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
when size_of(E) == 0 {
@@ -606,6 +633,9 @@ append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #ca
}
}
// `non_zero_append_elems` appends `args` to the end of a dynamic array, without zeroing any reserved memory
//
// Note: Prefer using the procedure group `non_zero_append
@builtin
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
when size_of(E) == 0 {
@@ -622,10 +652,16 @@ _append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_ze
return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
}
// `append_elem_string` appends a string to the end of a dynamic array of bytes
//
// Note: Prefer using the procedure group `append`.
@builtin
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elem_string(array, arg, true, loc)
}
// `non_zero_append_elem_string` appends a string to the end of a dynamic array of bytes, without zeroing any reserved memory
//
// Note: Prefer using the procedure group `non_zero_append`.
@builtin
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elem_string(array, arg, false, loc)
@@ -633,6 +669,8 @@ non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, l
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
//
// Note: Prefer using the procedure group `append`.
@builtin
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
n_arg: int
@@ -647,7 +685,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
}
// The append built-in procedure appends elements to the end of a dynamic array
@builtin append :: proc{
@builtin
append :: proc{
append_elem,
append_elems,
append_elem_string,
@@ -656,7 +695,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
append_soa_elems,
}
@builtin non_zero_append :: proc{
@builtin
non_zero_append :: proc{
non_zero_append_elem,
non_zero_append_elems,
non_zero_append_elem_string,
@@ -666,6 +706,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
}
// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible,
// whatever `err` happens to be.
@builtin
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
@@ -677,6 +719,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
}
// `inject_at_elem` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across"
@builtin
inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
when !ODIN_NO_BOUNDS_CHECK {
@@ -698,6 +741,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
return
}
// `inject_at_elems` injects multiple elements in a dynamic array at a specified index and moves the previous elements after that index "across"
@builtin
inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
when !ODIN_NO_BOUNDS_CHECK {
@@ -724,6 +768,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
return
}
// `inject_at_elem_string` injects a string into a dynamic array at a specified index and moves the previous elements after that index "across"
@builtin
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
when !ODIN_NO_BOUNDS_CHECK {
@@ -748,10 +793,13 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
return
}
// `inject_at` injects something into a dynamic array at a specified index and moves the previous elements after that index "across"
@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
// `assign_at_elem` assigns a value at a given index. If the requested index is smaller than the current
// size of the dynamic array, it will attempt to `resize` the a new length of `index+1` and then assign as `index`.
@builtin
assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if index < len(array) {
@@ -766,6 +814,8 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc
}
// `assign_at_elems` assigns a values at a given index. If the requested index is smaller than the current
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(args)` and then assign as `index`.
@builtin
assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
new_size := index + len(args)
@@ -782,7 +832,8 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
return
}
// `assign_at_elem_string` assigns a string at a given index. If the requested index is smaller than the current
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(arg)` and then assign as `index`.
@builtin
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
new_size := index + len(arg)
@@ -799,7 +850,14 @@ assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
return
}
@builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string}
// `assign_at` assigns a value at a given index. If the requested index is smaller than the current
// size of the dynamic array, it will attempt to `resize` the a new length of `index+size_needed` and then assign as `index`.
@builtin
assign_at :: proc{
assign_at_elem,
assign_at_elems,
assign_at_elem_string,
}
@@ -816,8 +874,10 @@ 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`).
//
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
//
// 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
}
@@ -850,18 +910,28 @@ _reserve_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem
return nil
}
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
//
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
//
// Note: Prefer the procedure group `reserve`.
@builtin
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc)
}
// `non_zero_reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
//
// When a memory resize allocation is required, the memory will be asked to not be zeroed (i.e. it calls `non_zero_mem_resize`).
//
// Note: Prefer the procedure group `non_zero_reserve`.
@builtin
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc)
}
_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
}
@@ -903,28 +973,33 @@ _resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem,
// `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`).
//
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
//
// Note: Prefer the procedure group `resize`
@builtin
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc)
}
// `non_zero_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`).
//
// When a memory resize allocation is required, the memory will be asked to not be zeroed (i.e. it calls `non_zero_mem_resize`).
//
// Note: Prefer the procedure group `non_zero_resize`
@builtin
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
}
/*
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
If `new_cap` is negative, then `len(array)` is used.
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`
*/
// Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
//
// If `new_cap` is negative, then `len(array)` is used.
//
// 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, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
}
@@ -1005,6 +1080,7 @@ map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr
}
// `card` returns the number of bits that are set in a bit_set—its cardinality
@builtin
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
@@ -1012,6 +1088,10 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
// Evaluates the condition and panics the program iff the condition is false.
// This uses the `context.assertion_failure_procedure` to assert.
//
// This routine will be ignored when `ODIN_DISABLE_ASSERT` is true.
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
@@ -1032,9 +1112,9 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc :=
}
}
// Evaluates the condition and aborts the program iff the condition is
// false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
// execute.
// Evaluates the condition and panics the program iff the condition is false.
// This uses the `context.assertion_failure_procedure` to assert.
// This routine ignores `ODIN_DISABLE_ASSERT`, and will always execute.
@builtin
ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
if !condition {
@@ -1050,6 +1130,8 @@ ensure :: proc(condition: bool, message := #caller_expression(condition), loc :=
}
}
// Panics the program with a message.
// This uses the `context.assertion_failure_procedure` to panic.
@builtin
panic :: proc(message: string, loc := #caller_location) -> ! {
p := context.assertion_failure_proc
@@ -1059,6 +1141,8 @@ panic :: proc(message: string, loc := #caller_location) -> ! {
p("panic", message, loc)
}
// Panics the program with a message to indicate something has yet to be implemented.
// This uses the `context.assertion_failure_procedure` to assert.
@builtin
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
p := context.assertion_failure_proc
@@ -1068,7 +1152,10 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
p("not yet implemented", message, loc)
}
// Evaluates the condition and panics the program iff the condition is false.
// This uses the `default_assertion_contextless_failure_proc` to assert.
//
// This routine will be ignored when `ODIN_DISABLE_ASSERT` is true.
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
@@ -1085,6 +1172,8 @@ assert_contextless :: proc "contextless" (condition: bool, message := #caller_ex
}
}
// Evaluates the condition and panics the program iff the condition is false.
// This uses the `default_assertion_contextless_failure_proc` to assert.
@builtin
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
if !condition {
@@ -1096,11 +1185,15 @@ ensure_contextless :: proc "contextless" (condition: bool, message := #caller_ex
}
}
// Panics the program with a message to indicate something has yet to be implemented.
// This uses the `default_assertion_contextless_failure_proc` to assert.
@builtin
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
default_assertion_contextless_failure_proc("panic", message, loc)
}
// Panics the program with a message.
// This uses the `default_assertion_contextless_failure_proc` to assert.
@builtin
unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
default_assertion_contextless_failure_proc("not yet implemented", message, loc)

View File

@@ -23,6 +23,14 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, .None
}
// nil_allocator returns an allocator which will return `nil` for any result.
// * `.Alloc`, `.Alloc_Non_Zero`, `.Resize`, `.Resize_Non_Zeroed` will return `nil, .Out_Of_Memory`
// * `.Free` will return `nil, .None`
// * `.Free_All` will return `nil, .Mode_Not_Implemented`
// * `.Query_Features`, `.Query_Info` will return `nil, .Mode_Not_Implemented`
//
// This is extremely useful for creating a dynamic array from a buffer which does not nothing
// on a resize/reserve beyond the originally allocated memory.
@(require_results)
nil_allocator :: proc "contextless" () -> Allocator {
return Allocator{
@@ -73,6 +81,9 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil
}
// panic_allocator returns an allocator which will panic for any non-zero-sized allocation or `query_info`
//
// This is extremely useful for to check when something does a memory operation when it should not, and thus panic.
@(require_results)
panic_allocator :: proc() -> Allocator {
return Allocator{

View File

@@ -4,6 +4,7 @@ DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKIN
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_DEFAULT_TO_NIL_ALLOCATOR
when NO_DEFAULT_TEMP_ALLOCATOR {
// `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`.
Default_Temp_Allocator :: struct {}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
@@ -20,6 +21,11 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
}
} else {
// `Default_Temp_Allocator` is an `Arena` based type of allocator. See `runtime.Arena` for its implementation.
// The default `context.temp_allocator` is typically called with `free_all(context.temp_allocator)` once per "frame-loop"
// to prevent it from "leaking" memory.
//
// Note: `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`.
Default_Temp_Allocator :: struct {
arena: Arena,
}

View File

@@ -985,6 +985,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)
}

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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

View File

@@ -15,16 +15,25 @@ objc_SEL :: ^intrinsics.objc_selector
objc_Ivar :: ^intrinsics.objc_ivar
objc_BOOL :: bool
objc_super :: struct {
receiver: objc_id,
super_class: objc_Class,
}
objc_IMP :: proc "c" (object: objc_id, sel: objc_SEL, #c_vararg args: ..any) -> objc_id
foreign ObjC {
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
// See: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-abi.h#L111
objc_msgSendSuper2 :: proc "c" (super: rawptr, op: objc_SEL, #c_vararg args: ..any) -> objc_id ---
objc_msgSendSuper2_stret :: proc "c" (super: ^objc_super, op: objc_SEL, #c_vararg args: ..any) ---
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
@@ -33,6 +42,7 @@ foreign ObjC {
class_addIvar :: proc "c" (cls: objc_Class, name: cstring, size: uint, alignment: u8, types: cstring) -> objc_BOOL ---
class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar ---
class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint ---
class_getSuperclass :: proc "c" (cls : objc_Class) -> objc_Class ---
ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr ---
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
}

View File

@@ -28,7 +28,8 @@ error() {
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
if [ -n "$(command -v $(brew --prefix llvm@20)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@20)/bin/llvm-config"
if [ -n "$(command -v $(brew --prefix llvm@21)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@21)/bin/llvm-config"
elif [ -n "$(command -v $(brew --prefix llvm@20)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@20)/bin/llvm-config"
elif [ -n "$(command -v $(brew --prefix llvm@19)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@19)/bin/llvm-config"
elif [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
elif [ -n "$(command -v $(brew --prefix llvm@17)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@17)/bin/llvm-config"
@@ -38,23 +39,19 @@ fi
if [ -z "$LLVM_CONFIG" ]; then
# darwin, linux, openbsd
if [ -n "$(command -v llvm-config-20)" ]; then LLVM_CONFIG="llvm-config-20"
if [ -n "$(command -v llvm-config-21)" ]; then LLVM_CONFIG="llvm-config-21"
elif [ -n "$(command -v llvm-config-20)" ]; then LLVM_CONFIG="llvm-config-20"
elif [ -n "$(command -v llvm-config-19)" ]; then LLVM_CONFIG="llvm-config-19"
elif [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
# freebsd
elif [ -n "$(command -v llvm-config21)" ]; then LLVM_CONFIG="llvm-config21"
elif [ -n "$(command -v llvm-config20)" ]; then LLVM_CONFIG="llvm-config20"
elif [ -n "$(command -v llvm-config19)" ]; then LLVM_CONFIG="llvm-config19"
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config13"
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config12"
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config11"
# fallback
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
else
@@ -75,18 +72,12 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 20 ]; then
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17, 18, 19 or 20"
if [ $LLVM_VERSION_MAJOR -lt 14 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 21 ]; then
error "Invalid LLVM version $LLVM_VERSION: must be 14, 17, 18, 19, 20, or 21"
fi
case "$OS_NAME" in
Darwin)
if [ "$OS_ARCH" = "arm64" ]; then
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17, 18, 19 or 20"
fi
fi
darwin_sysroot=
if [ $(which xcrun) ]; then
darwin_sysroot="--sysroot $(xcrun --sdk macosx --show-sdk-path)"

View File

@@ -1 +1,6 @@
comment: false
comment: false
coverage:
status:
project:
default:
threshold: 1%

2
core/bufio/doc.odin Normal file
View File

@@ -0,0 +1,2 @@
// Wraps an `io.Stream` interface to provide buffered I/O.
package bufio

View File

@@ -29,6 +29,7 @@ MIN_READ_BUFFER_SIZE :: 16
@(private)
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
// reader_init initializes using an `allocator`
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
size := size
size = max(size, MIN_READ_BUFFER_SIZE)
@@ -37,6 +38,7 @@ reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, all
b.buf = make([]byte, size, allocator, loc)
}
// reader_init initializes using a user provided bytes buffer `buf`
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
reader_reset(b, rd)
b.buf_allocator = {}
@@ -49,10 +51,12 @@ reader_destroy :: proc(b: ^Reader) {
b^ = {}
}
// reader_size returns the number of bytes in the backing buffer
reader_size :: proc(b: ^Reader) -> int {
return len(b.buf)
}
// reader_reset resets the read and write positions, and the error values
reader_reset :: proc(b: ^Reader, r: io.Reader) {
b.rd = r
b.r, b.w = 0, 0

View File

@@ -46,6 +46,7 @@ DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16
@(private)
_INIT_BUF_SIZE :: 4096
// Initializes a Scanner buffer an allocator `buf_allocator`
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
s.r = r
s.split = scan_lines
@@ -53,6 +54,8 @@ scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocat
s.buf.allocator = buf_allocator
return s
}
// Initializes a Scanner buffer a user provided bytes buffer `buf`
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
s.r = r
s.split = scan_lines
@@ -75,24 +78,27 @@ scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
return s._err
}
// Returns the most recent token created by scanner_scan.
// Returns the most recent token created by 'scan'.
// The underlying array may point to data that may be overwritten
// by another call to scanner_scan.
// by another call to 'scan'.
// Treat the returned value as if it is immutable.
scanner_bytes :: proc(s: ^Scanner) -> []byte {
return s.token
}
// Returns the most recent token created by scanner_scan.
// Returns the most recent token created by 'scan'.
// The underlying array may point to data that may be overwritten
// by another call to scanner_scan.
// by another call to 'scan'.
// Treat the returned value as if it is immutable.
scanner_text :: proc(s: ^Scanner) -> string {
return string(s.token)
}
// scanner_scan advances the scanner
scanner_scan :: proc(s: ^Scanner) -> bool {
// scanner_scan is an alias of scan
scanner_scan :: scan
// scan advances the Scanner
scan :: proc(s: ^Scanner) -> bool {
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
switch s._err {
case nil, .EOF:
@@ -229,6 +235,7 @@ scanner_scan :: proc(s: ^Scanner) -> bool {
}
}
// scan_bytes is a splitting procedure that returns each byte as a token
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
if at_eof && len(data) == 0 {
return
@@ -236,6 +243,10 @@ scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
return 1, data[0:1], nil, false
}
// scan_runes is a splitting procedure that returns each UTF-8 encoded rune as a token.
// The lsit of runes return is equivalent to that of iterating over a string in a 'for in' loop, meaning any
// erroneous UTF-8 encodings will be returned as U+FFFD. Unfortunately this means it is impossible for the "client"
// to know whether a U+FFFD is an expected replacement rune or an encoding of an error.
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
if at_eof && len(data) == 0 {
return
@@ -264,7 +275,8 @@ scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
token = ERROR_RUNE
return
}
// scan_words is a splitting procedure that returns each Unicode-space-separated word of text, excluding the surrounded spaces.
// It will never return return an empty string.
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
is_space :: proc "contextless" (r: rune) -> bool {
switch r {
@@ -312,6 +324,8 @@ scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
return
}
// scan_lines is a splitting procedure that returns each line of text stripping of any trailing newline and an optional preceding carriage return (\r?\n).
// A new line is allowed to be empty.
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {

View File

@@ -19,6 +19,7 @@ Writer :: struct {
}
// Initialized a Writer with an `allocator`
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
size := size
size = max(size, MIN_READ_BUFFER_SIZE)
@@ -27,6 +28,7 @@ writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, all
b.buf = make([]byte, size, allocator)
}
// Initialized a Writer with a user provided buffer `buf`
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
writer_reset(b, wr)
b.buf_allocator = {}

View File

@@ -1,3 +1,4 @@
// Procedures for manipulation of `[]byte` slices.
package bytes
import "base:intrinsics"
@@ -134,8 +135,13 @@ equal_fold :: proc(u, v: []byte) -> bool {
return false
}
// TODO(bill): Unicode folding
r := unicode.simple_fold(sr)
for r != sr && r < tr {
r = unicode.simple_fold(sr)
}
if r == tr {
continue loop
}
return false
}

View File

@@ -1,3 +1,4 @@
// Defines the basic types used by `C` programs for foreign function and data structure interop.
package c
import builtin "base:builtin"
@@ -48,7 +49,7 @@ int_least64_t :: builtin.i64
uint_least64_t :: builtin.u64
// Same on Windows, Linux, and FreeBSD
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
when ODIN_ARCH == .i386 {
int_fast8_t :: builtin.i8
uint_fast8_t :: builtin.u8
int_fast16_t :: builtin.i32
@@ -57,6 +58,15 @@ when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
uint_fast32_t :: builtin.u32
int_fast64_t :: builtin.i64
uint_fast64_t :: builtin.u64
} else when ODIN_ARCH == .amd64 {
int_fast8_t :: builtin.i8
uint_fast8_t :: builtin.u8
int_fast16_t :: long
uint_fast16_t :: ulong
int_fast32_t :: long
uint_fast32_t :: ulong
int_fast64_t :: builtin.i64
uint_fast64_t :: builtin.u64
} else {
int_fast8_t :: builtin.i8
uint_fast8_t :: builtin.u8

2
core/c/libc/doc.odin Normal file
View File

@@ -0,0 +1,2 @@
// Declares the commonly used things in `libc` (`C` standard library).
package libc

View File

@@ -275,7 +275,7 @@ foreign libc {
// 7.21.7 Character input/output functions
fgetc :: proc(stream: ^FILE) -> int ---
fgets :: proc(s: [^]char, n: int, stream: ^FILE) -> [^]char ---
fputc :: proc(s: cstring, stream: ^FILE) -> int ---
fputc :: proc(s: c.int, stream: ^FILE) -> int ---
getc :: proc(stream: ^FILE) -> int ---
getchar :: proc() -> int ---
putc :: proc(c: int, stream: ^FILE) -> int ---

View File

@@ -1,3 +1,6 @@
// A collection of utilities to aid with other `compress`ion packages.
package compress
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -6,10 +9,6 @@
Jeroen van Rijn: Initial implementation, optimization.
*/
// package compress is a collection of utilities to aid with other compression packages
package compress
import "core:io"
import "core:bytes"
import "base:runtime"

View File

@@ -1,15 +1,6 @@
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
A small `GZIP` unpacker.
List of contributors:
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
A small GZIP implementation as an example.
*/
/*
Example:
import "core:bytes"
import "core:os"
@@ -88,3 +79,14 @@ Example:
}
*/
package compress_gzip
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
A small GZIP implementation as an example.
*/

View File

@@ -1,11 +1,11 @@
package compress_shoco
/*
This file was generated, so don't edit this by hand.
Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h,
which is an English word model.
*/
package compress_shoco
DEFAULT_MODEL :: Shoco_Model {
min_char = 39,
max_char = 122,

View File

@@ -1,3 +1,6 @@
// `Shoco` short string compression and decompression.
package compress_shoco
/*
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -8,9 +11,6 @@
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
*/
// package shoco is an implementation of the shoco short string compressor.
package compress_shoco
import "base:intrinsics"
import "core:compress"

View File

@@ -1,14 +1,6 @@
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
`Deflate` decompression of raw and `ZLIB`-type streams.
List of contributors:
Jeroen van Rijn: Initial implementation.
An example of how to use `zlib.inflate`.
*/
/*
Example:
package main
@@ -49,3 +41,13 @@ Example:
}
*/
package compress_zlib
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
An example of how to use `zlib.inflate`.
*/

View File

@@ -1,8 +1,4 @@
/*
package avl implements an AVL tree.
The implementation is non-intrusive, and non-recursive.
*/
// A non-intrusive and non-recursive implementation of `AVL` trees.
package container_avl
@(require) import "base:intrinsics"

View File

@@ -1,7 +1,9 @@
/*
A dynamically-sized array of bits.
The Bit Array can be used in several ways:
By default you don't need to instantiate a Bit Array.
By default you don't need to instantiate a `Bit_Array`.
Example:
package test
@@ -18,11 +20,11 @@ Example:
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
was_set, was_retrieved := get(&bits, -1)
fmt.println(was_set, was_retrieved)
fmt.println(was_set, was_retrieved)
destroy(&bits)
}
A Bit Array can optionally allow for negative indices, if the minimum value was given during creation.
A `Bit_Array` can optionally allow for negative indices, if the minimum value was given during creation.
Example:
package test
@@ -49,4 +51,4 @@ Example:
fmt.printf("Freed.\n")
}
*/
package container_dynamic_bit_array
package container_dynamic_bit_array

View File

@@ -1,7 +1,7 @@
/*
Package list implements an intrusive doubly-linked list.
An intrusive doubly-linked list.
An intrusive container requires a `Node` to be embedded in your own structure, like this.
The intrusive container requires a `Node` to be embedded in your own structure, like this.
Example:
My_String :: struct {
node: list.Node,
@@ -46,4 +46,4 @@ Output:
Hello
World
*/
package container_intrusive_list
package container_intrusive_list

View File

@@ -1,3 +1,4 @@
// A least-recently-used (`LRU`) cache. It automatically removes older entries if its capacity is reached.
package container_lru
import "base:runtime"

View File

@@ -1,3 +1,4 @@
// A priority queue data structure.
package container_priority_queue
import "base:builtin"

View File

@@ -1,3 +1,4 @@
// A dynamically resizable double-ended queue/ring-buffer.
package container_queue
import "base:builtin"

View File

@@ -1,4 +1,4 @@
// This package implements a red-black tree
// A red-black tree with the same API as our AVL tree.
package container_rbtree
@(require) import "base:intrinsics"
@@ -128,9 +128,9 @@ find_value :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bo
return
}
// find_or_insert attempts to insert the value into the tree, and returns
// the node, a boolean indicating if the value was inserted, and the
// node allocator error if relevant. If the value is already present, the existing node is updated.
// find_or_insert attempts to insert the key-value pair into the tree, and returns
// the node, a boolean indicating if a new node was inserted, and the
// node allocator error if relevant. If the key is already present, the existing node is updated and returned.
find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n: ^Node(Key, Value), inserted: bool, err: runtime.Allocator_Error) {
n_ptr := &t._root
for n_ptr^ != nil {
@@ -141,6 +141,7 @@ find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n:
case .Greater:
n_ptr = &n._right
case .Equal:
n.value = value
return
}
}

View File

@@ -1,8 +1,7 @@
/*
Package small_array implements a dynamic array like
interface on a stack-allocated, fixed-size array.
A dynamic array-like interface on a stack-allocated, fixed-size array.
The Small_Array type is optimal for scenarios where you need
The `Small_Array` type is optimal for scenarios where you need
a container for a fixed number of elements of a specific type,
with the total number known at compile time but the exact
number to be used determined at runtime.
@@ -33,7 +32,7 @@ Example:
return
}
// the Small_Array can be an ordinary parameter 'generic' over
// the `Small_Array` can be an ordinary parameter 'generic' over
// the actual length to be usable with different sizes
print_elements :: proc(arr: ^small_array.Small_Array($N, rune)) {
for r in small_array.slice(arr) {
@@ -52,4 +51,4 @@ Output:
Hellope
*/
package container_small_array
package container_small_array

View File

@@ -1,8 +1,8 @@
package container_small_array
import "base:builtin"
import "base:runtime"
_ :: runtime
@require import "base:intrinsics"
@require import "base:runtime"
/*
A fixed-size stack-allocated array operated on in a dynamic fashion.
@@ -169,7 +169,7 @@ Output:
x
*/
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
get_safe :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
if index < 0 || index >= a.len {
return {}, false
}
@@ -183,11 +183,11 @@ Get a pointer to the item at the specified position.
- `a`: A pointer to the small-array
- `index`: The position of the item to get
**Returns**
**Returns**
- the pointer to the element at the specified position
- true if element exists, false otherwise
*/
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
get_ptr_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
if index < 0 || index >= a.len {
return {}, false
}
@@ -231,7 +231,7 @@ Example:
fmt.println(small_array.slice(&a))
// resizing makes the change visible
small_array.resize(&a, 100)
small_array.non_zero_resize(&a, 100)
fmt.println(small_array.slice(&a))
}
@@ -250,6 +250,8 @@ set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
/*
Tries to resize the small-array to the specified length.
The memory of added elements will be zeroed out.
The new length will be:
- `length` if `length` <= capacity
- capacity if length > capacity
@@ -259,7 +261,7 @@ The new length will be:
- `length`: The new desired length
Example:
import "core:container/small_array"
import "core:fmt"
@@ -269,7 +271,7 @@ Example:
small_array.push_back(&a, 1)
small_array.push_back(&a, 2)
fmt.println(small_array.slice(&a))
small_array.resize(&a, 1)
fmt.println(small_array.slice(&a))
@@ -278,12 +280,56 @@ Example:
}
Output:
[1, 2]
[1]
[1, 0, 0, 0, 0]
*/
resize :: proc "contextless" (a: ^$A/Small_Array($N, $T), length: int) {
prev_len := a.len
a.len = min(length, builtin.len(a.data))
if prev_len < a.len {
intrinsics.mem_zero(&a.data[prev_len], size_of(T)*(a.len-prev_len))
}
}
/*
Tries to resize the small-array to the specified length.
The new length will be:
- `length` if `length` <= capacity
- capacity if length > capacity
**Inputs**
- `a`: A pointer to the small-array
- `length`: The new desired length
Example:
import "core:container/small_array"
import "core:fmt"
non_zero_resize :: proc() {
a: small_array.Small_Array(5, int)
small_array.push_back(&a, 1)
small_array.push_back(&a, 2)
fmt.println(small_array.slice(&a))
small_array.non_zero_resize(&a, 1)
fmt.println(small_array.slice(&a))
small_array.non_zero_resize(&a, 100)
fmt.println(small_array.slice(&a))
}
Output:
[1, 2]
[1]
[1, 2, 0, 0, 0]
*/
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
non_zero_resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
a.len = min(length, builtin.len(a.data))
}

View File

@@ -1,6 +1,5 @@
// The following is a generic O(V+E) topological sorter implementation.
// This is the fastest known method for topological sorting and Odin's
// map type is being used to accelerate lookups.
// A generic `O(V+E)` topological sorter implementation. This is the fastest known method for topological sorting.
// Odin's map type is being used to accelerate lookups.
package container_topological_sort
import "base:intrinsics"

View File

@@ -1,6 +1,5 @@
/*
package aead provides a generic interface to the supported Authenticated
Encryption with Associated Data algorithms.
A generic interface to Authenticated Encryption with Associated Data (`AEAD`) algorithms.
Both a one-shot and context based interface are provided, with similar
usage. If multiple messages are to be sealed/opened via the same key,
@@ -54,4 +53,4 @@ Example:
assert(bytes.equal(opened_pt, plaintext))
}
*/
package aead
package aead

View File

@@ -1,6 +1,7 @@
/*
package aegis implements the AEGIS-128L and AEGIS-256 Authenticated
Encryption with Additional Data algorithms.
`AEGIS-128L` and `AEGIS-256` AEAD algorithms.
Where AEAD stands for Authenticated Encryption with Additional Data.
See:
- [[ https://www.ietf.org/archive/id/draft-irtf-cfrg-aegis-aead-12.txt ]]

View File

@@ -1,5 +1,5 @@
/*
package aes implements the AES block cipher and some common modes.
The `AES` block cipher and some common modes.
See:
- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf ]]

View File

@@ -1,5 +1,5 @@
/*
package blake2b implements the BLAKE2b hash algorithm.
`BLAKE2b` hash algorithm.
See:
- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]

View File

@@ -1,5 +1,5 @@
/*
package blake2s implements the BLAKE2s hash algorithm.
`BLAKE2s` hash algorithm.
See:
- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]

View File

@@ -1,5 +1,5 @@
/*
package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
`ChaCha20` and `XChaCha20` stream ciphers.
See:
- [[ https://datatracker.ietf.org/doc/html/rfc8439 ]]

View File

@@ -1,7 +1,7 @@
/*
package chacha20poly1305 implements the AEAD_CHACHA20_POLY1305 and
AEAD_XChaCha20_Poly1305 Authenticated Encryption with Additional Data
algorithms.
`AEAD_CHACHA20_POLY1305` and `AEAD_XChaCha20_Poly1305` algorithms.
Where AEAD stands for Authenticated Encryption with Additional Data.
See:
- [[ https://www.rfc-editor.org/rfc/rfc8439 ]]

View File

@@ -1,7 +1,4 @@
/*
package crypto implements a selection of cryptography algorithms and useful
helper routines.
*/
// A selection of cryptography algorithms and useful helper routines.
package crypto
import "base:runtime"

View File

@@ -1,6 +1,5 @@
/*
package deoxysii implements the Deoxys-II-256 Authenticated Encryption
with Additional Data algorithm.
`Deoxys-II-256` Authenticated Encryption with Additional Data (`AEAD`) algorithm.
- [[ https://sites.google.com/view/deoxyscipher ]]
- [[ https://thomaspeyrin.github.io/web/assets/docs/papers/Jean-etal-JoC2021.pdf ]]

View File

@@ -1,5 +1,5 @@
/*
package ed25519 implements the Ed25519 EdDSA signature algorithm.
`Ed25519` EdDSA signature algorithm.
See:
- [[ https://datatracker.ietf.org/doc/html/rfc8032 ]]

View File

@@ -1,5 +1,5 @@
/*
package hash provides a generic interface to the supported hash algorithms.
A generic interface to the supported hash algorithms.
A high-level convenience procedure group `hash` is provided to easily
accomplish common tasks.

View File

@@ -1,6 +1,5 @@
/*
package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
Derivation Function.
`HKDF` HMAC-based Extract-and-Expand Key Derivation Function.
See: [[ https://www.rfc-editor.org/rfc/rfc5869 ]]
*/

View File

@@ -1,5 +1,5 @@
/*
package hmac implements the HMAC MAC algorithm.
`HMAC` message authentication code (`MAC`) algorithm.
See:
- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf ]]

View File

@@ -1,5 +1,5 @@
/*
package kmac implements the KMAC MAC algorithm.
`KMAC` message authentication code (`MAC`) algorithm.
See:
- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]

View File

@@ -1,5 +1,5 @@
/*
package keccak implements the Keccak hash algorithm family.
`Keccak` hash algorithm family.
During the SHA-3 standardization process, the padding scheme was changed
thus Keccac and SHA-3 produce different outputs. Most users should use

View File

@@ -1,5 +1,5 @@
/*
package md5 implements the MD5 hash algorithm.
`MD5` hash algorithm.
WARNING: The MD5 algorithm is known to be insecure and should only be
used for interoperating with legacy applications.

View File

@@ -1,5 +1,5 @@
/*
package sha1 implements the SHA1 hash algorithm.
`SHA1` hash algorithm.
WARNING: The SHA1 algorithm is known to be insecure and should only be
used for interoperating with legacy applications.

View File

@@ -1,5 +1,5 @@
/*
package pbkdf2 implements the PBKDF2 password-based key derivation function.
`PBKDF2` password-based key derivation function.
See: [[ https://www.rfc-editor.org/rfc/rfc2898 ]]
*/

View File

@@ -1,5 +1,5 @@
/*
package poly1305 implements the Poly1305 one-time MAC algorithm.
`Poly1305` one-time MAC algorithm.
See:
- [[ https://datatracker.ietf.org/doc/html/rfc8439 ]]

View File

@@ -1,5 +1,5 @@
/*
package ristretto255 implement the ristretto255 prime-order group.
Ristretto255 prime-order group.
See:
- [[ https://www.rfc-editor.org/rfc/rfc9496 ]]

View File

@@ -1,5 +1,5 @@
/*
package sha2 implements the SHA2 hash algorithm family.
`SHA2` hash algorithm family.
See:
- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf ]]

View File

@@ -1,5 +1,5 @@
/*
package sha3 implements the SHA3 hash algorithm family.
`SHA3` hash algorithm family.
The SHAKE XOF can be found in crypto/shake. While discouraged if the
pre-standardization Keccak algorithm is required, it can be found in

View File

@@ -1,5 +1,5 @@
/*
package shake implements the SHAKE and cSHAKE XOF algorithm families.
`SHAKE` and `cSHAKE` XOF algorithm families.
The SHA3 hash algorithm can be found in the crypto/sha3.

View File

@@ -1,5 +1,5 @@
/*
package siphash Implements the SipHash hashing algorithm.
`SipHash` hashing algorithm.
Use the specific procedures for a certain setup. The generic procedures will default to Siphash 2-4.

View File

@@ -1,5 +1,5 @@
/*
package sm3 implements the SM3 hash algorithm.
`SM3` hash algorithm.
See:
- [[ https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02 ]]

View File

@@ -1,5 +1,5 @@
/*
package tuplehash implements the TupleHash and TupleHashXOF algorithms.
`TupleHash` and `TupleHashXOF` algorithms.
See:
- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]

View File

@@ -1,6 +1,5 @@
/*
package x25519 implements the X25519 (aka curve25519) Elliptic-Curve
Diffie-Hellman key exchange protocol.
`X25519` (aka `curve25519`) Elliptic-Curve Diffie-Hellman key exchange protocol.
See:
- [[ https://www.rfc-editor.org/rfc/rfc7748 ]]

View File

@@ -1,6 +1,5 @@
/*
package x448 implements the X448 (aka curve448) Elliptic-Curve
Diffie-Hellman key exchange protocol.
`X448` (aka `curve448`) Elliptic-Curve Diffie-Hellman key exchange protocol.
See:
- [[ https://www.rfc-editor.org/rfc/rfc7748 ]]

View File

@@ -1,3 +1,4 @@
// A reader for the Windows `PE` executable format for debug purposes.
package debug_pe
PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c

View File

@@ -1,5 +1,5 @@
/*
A debug stack trace library. Only works when debug symbols are enabled `-debug`.
Stack trace library. Only works when debug symbols are enabled using `-debug`.
Example:
import "base:runtime"

View File

@@ -1,5 +1,5 @@
/*
Package `core:dynlib` implements loading of shared libraries/DLLs and their symbols.
Cross-platform loading of shared libraries/DLLs and their symbols.
The behaviour of dynamically loaded libraries is specific to the target platform of the program.
For in depth detail on the underlying behaviour please refer to your target platform's documentation.

View File

@@ -44,4 +44,4 @@ main :: proc() {
fmt.println("84 - 13 =", sym.sub(84, 13))
fmt.println("hellope =", sym.hellope^)
}
}
}

View File

@@ -1,19 +1,20 @@
// Base32 encoding/decoding implementation as specified in RFC 4648.
// [[ More; https://www.rfc-editor.org/rfc/rfc4648.html ]]
/*
`Base32` encoding and decoding, as specified in [[ RFC 4648; https://www.rfc-editor.org/rfc/rfc4648.html ]].
A secondary param can be used to supply a custom alphabet to `encode` and a matching decoding table to `decode`.
If none is supplied it just uses the standard Base32 alphabet.
In case your specific version does not use padding, you may
truncate it from the encoded output.
Error represents errors that can occur during base32 decoding operations.
As per RFC 4648:
- Section 3.3: Invalid character handling
- Section 3.2: Padding requirements
- Section 6: Base32 encoding specifics (including block size requirements)
*/
package encoding_base32
// @note(zh): Encoding utility for Base32
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base32 alphabet.
// In case your specific version does not use padding, you may
// truncate it from the encoded output.
// Error represents errors that can occur during base32 decoding operations.
// As per RFC 4648:
// - Section 3.3: Invalid character handling
// - Section 3.2: Padding requirements
// - Section 6: Base32 encoding specifics (including block size requirements)
Error :: enum {
None,
Invalid_Character, // Input contains characters outside the specified alphabet

View File

@@ -1,16 +1,18 @@
/*
`Base64` encoding and decoding.
A secondary param can be used to supply a custom alphabet to `encode` and a matching decoding table to `decode`.
If none is supplied it just uses the standard Base64 alphabet.
In case your specific version does not use padding, you may
truncate it from the encoded output.
*/
package encoding_base64
import "core:io"
import "core:mem"
import "core:strings"
// @note(zh): Encoding utility for Base64
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base64 alphabet.
// Incase your specific version does not use padding, you may
// truncate it from the encoded output.
ENC_TABLE := [64]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',

View File

@@ -1,5 +1,6 @@
/*
Package cbor encodes, decodes, marshals and unmarshals types from/into RCF 8949 compatible CBOR binary.
Encodes and decodes types from/into [[ RCF 8949; https://www.rfc-editor.org/rfc/rfc8949.html ]] compatible `CBOR` binary.
Also provided are conversion to and from JSON and the CBOR diagnostic format.
**Allocations:**
@@ -165,5 +166,4 @@ Output:
"str": "Hello, World!"
}
*/
package encoding_cbor
package encoding_cbor

View File

@@ -1,6 +1,5 @@
/*
package csv reads and writes comma-separated values (CSV) files.
This package supports the format described in [[ RFC 4180; https://tools.ietf.org/html/rfc4180.html ]]
Reader and writer for comma-separated values (`CSV`) files, per [[ RFC 4180 ; https://tools.ietf.org/html/rfc4180.html ]].
Example:
package main

View File

@@ -1,6 +1,5 @@
/*
Package endian implements a simple translation between bytes and numbers with
specific endian encodings.
A simple translation between bytes and numbers with specific endian encodings.
Example:
buf: [100]u8

View File

@@ -1,13 +1,5 @@
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
*/
/*
A unicode entity encoder/decoder.
Encode and decode `rune`s to/from a Unicode `&entity;`.
This code has several procedures to map unicode runes to/from different textual encodings.
- SGML/XML/HTML entity
@@ -21,6 +13,14 @@
*/
package encoding_unicode_entity
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
*/
import "core:unicode/utf8"
import "core:unicode"
import "core:strings"

View File

@@ -1,3 +1,4 @@
// Encoding and decoding of hex-encoded binary, e.g. `0x23` -> `#`.
package encoding_hex
import "core:io"

View File

@@ -1,5 +1,6 @@
/*
Implementation of the HxA 3D asset format
Eskil Steenberg's `HxA` 3D asset interchange format.
HxA is a interchangeable graphics asset format.
Designed by Eskil Steenberg. @quelsolaar / eskil 'at' obsession 'dot' se / www.quelsolaar.com

View File

@@ -1,3 +1,4 @@
// Reader and writer for a variant of the `.ini` file format with `key = value` entries in `[sections]`.
package encoding_ini
import "base:runtime"

View File

@@ -176,7 +176,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
return .Unsupported_Type
case runtime.Type_Info_Pointer:
return .Unsupported_Type
if v.id == typeid_of(Null) {
io.write_string(w, "null") or_return
} else {
return .Unsupported_Type
}
case runtime.Type_Info_Multi_Pointer:
return .Unsupported_Type

View File

@@ -1,14 +1,13 @@
package encoding_json
import "core:strings"
/*
JSON
Encoding and decoding JSON in strict `JSON`, [[ JSON5 ; https://json5.org/ ]] and [[ BitSquid ; https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html ]] variants.
Using one of these `Specification`s.
JSON
strict JSON
JSON5
JSON5
pure superset of JSON and valid JavaScript
https://json5.org/
* Object keys may be an ECMAScript 5.1 IdentifierName.
* Objects may have a single trailing comma.
* Arrays may have a single trailing comma.
@@ -21,17 +20,21 @@ import "core:strings"
* Numbers may begin with an explicit plus sign.
* Single and multi-line comments are allowed.
* Additional white space characters are allowed.
MJSON
pure superset of JSON5, may not be valid JavaScript
https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
* All the same features as JSON5 plus extras.
* Assume an object definition at the root level (no need to surround entire file with { } ).
* Commas are optional, using comma insertion rules with newlines.
* Quotes around object keys are optional if the keys are valid identifiers.
* : can be replaced with =
*/
package encoding_json
import "core:strings"
Specification :: enum {
JSON,
JSON5, // https://json5.org/

View File

@@ -1,6 +1,5 @@
/*
package uuid implements Universally Unique Identifiers according to the
standard originally outlined in RFC 4122 with additions from RFC 9562.
Universally Unique Identifiers (`UUID`) according to [[ RFC 4122 ; https://tools.ietf.org/html/rfc4122.html ]], with additions from [[ RFC 9562 ; https://tools.ietf.org/html/rfc9562.html ]].
The UUIDs are textually represented and read in the following string format:
`00000000-0000-v000-V000-000000000000`

View File

@@ -1,7 +1,6 @@
/*
package uuid/legacy implements versions 3 and 5 of UUID generation, both of
which are using hashing algorithms (MD5 and SHA1, respectively) that are known
these days to no longer be secure.
Versions 3 and 5 of `UUID` generation, both of which use legacy (`MD5` + `SHA1`) hashes.
Those are known these days to no longer be secure.
*/
package uuid_legacy

View File

@@ -1,7 +1,5 @@
/*
Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others.
Author of this Odin package: Jeroen van Rijn
`LEB128` variable integer encoding and decoding, as used by `DWARF` & `DEX` files.
Example:
package main
@@ -24,4 +22,4 @@ Example:
fmt.printf("Decoded as %v, using %v byte%v\n", decoded_val, decode_size, "" if decode_size == 1 else "s")
}
*/
package encoding_varint
package encoding_varint

View File

@@ -1,3 +1,5 @@
package encoding_varint
/*
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -6,8 +8,6 @@
Jeroen van Rijn: Initial implementation.
*/
package encoding_varint
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
// Instead we'll set limits on the values we'll encode/decode
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.

View File

@@ -1,7 +1,7 @@
/*
XML 1.0 / 1.1 parser
A parser for a useful subset of the `XML` specification.
A from-scratch XML implementation, loosely modelled on the [[ spec; https://www.w3.org/TR/2006/REC-xml11-20060816 ]].
A from-scratch `XML` implementation, loosely modelled on the [[ spec; https://www.w3.org/TR/2006/REC-xml11-20060816 ]].
Features:
- Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
@@ -11,8 +11,8 @@ Caveats:
- We do NOT support HTML in this package, as that may or may not be valid XML.
If it works, great. If it doesn't, that's not considered a bug.
- We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
- <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
- We do NOT support `UTF-16`. If you have a `UTF-16` XML file, please convert it to `UTF-8` first. Also, our condolences.
- `<[!ELEMENT` and `<[!ATTLIST` are not supported, and will be either ignored or return an error depending on the parser options.
MAYBE:
- XML writer?

View File

@@ -1,4 +1,7 @@
package encoding_xml
/*
An XML 1.0 / 1.1 parser
2021-2022 Jeroen van Rijn <nom@duclavier.com>.
available under Odin's BSD-3 license.
@@ -6,9 +9,6 @@
- Jeroen van Rijn: Initial implementation.
*/
package encoding_xml
// An XML 1.0 / 1.1 parser
import "core:bytes"
import "core:encoding/entity"
import "base:intrinsics"

View File

@@ -1,5 +1,5 @@
/*
package flags implements a command-line argument parser.
Command-line argument parser.
It works by using Odin's run-time type information to determine where and how
to store data on a struct provided by the program. Type conversion is handled

View File

@@ -1,5 +1,5 @@
/*
package fmt implements formatted I/O with procedures similar to C's printf and Python's format.
Formatted `I/O` with procedures similar to `C`'s printf and `Python`'s format.
The format 'verbs' are derived from C's but simpler.
Printing

View File

@@ -10,14 +10,14 @@ SomeType :: struct {
My_Custom_Base_Type :: distinct u32
main :: proc() {
// Ensure the fmt._user_formatters map is initialized
// Ensure the fmt._user_formatters map is initialized
fmt.set_user_formatters(new(map[typeid]fmt.User_Formatter))
// Register custom formatters for my favorite types
err := fmt.register_user_formatter(type_info_of(SomeType).id, SomeType_Formatter)
assert(err == .None)
assert(err == .None)
err = fmt.register_user_formatter(type_info_of(My_Custom_Base_Type).id, My_Custom_Base_Formatter)
assert(err == .None)
assert(err == .None)
// Use the custom formatters.
fmt.printfln("SomeType{{42}}: '%v'", SomeType{42})
@@ -53,5 +53,4 @@ My_Custom_Base_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool {
return false
}
return true
}
}

View File

@@ -1,10 +1,27 @@
package hash
/*
Compute CRC-16 in the manner of CCITT (ITU-T V.41), using the 0x1021 polynomial.
Generator polynomial: x^16 + x^12 + x^5 + 1
Used in the UDF (DVD *.iso) disk format's Volume Descriptor Tag,
and was more historically the ITU-T V.41 CRC-16 used in the XModem protocol,
which uses 0xffff as the initial value.
*/
@(optimization_mode="favor_size")
crc16_ccitt_0x1021 :: proc "contextless" (data: []u8, seed := u16(0)) -> (result: u16) #no_bounds_check {
result = seed
#no_bounds_check for b in data {
result = result << 8 ~ _ccitt_0x1021_table[(result >> 8) ~ u16(b)]
}
return result
}
@(optimization_mode="favor_size")
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
result = seed
#no_bounds_check for b in data {
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
result = result << 8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
}
return result
}
@@ -49,9 +66,7 @@ crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_
return u64(~result)
}
/*
Generator polynomial: x^64 + x^4 + x^3 + x + 1
*/
// Generator polynomial: x^64 + x^4 + x^3 + x + 1
@(optimization_mode="favor_size")
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
@@ -75,7 +90,8 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
return ~result
}
@private _crc64_table_ecma_182 := [256]u64{
@(private, rodata)
_crc64_table_ecma_182 := [256]u64{
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
@@ -142,7 +158,8 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
}
@private _crc64_table_xz := [8][256]u64le{
@(private, rodata)
_crc64_table_xz := [8][256]u64le{
{
0x0000000000000000, 0xb32e4cbe03a75f6f, 0xf4843657a840a05b, 0x47aa7ae9abe7ff34,
0x7bd0c384ff8f5e33, 0xc8fe8f3afc28015c, 0x8f54f5d357cffe68, 0x3c7ab96d5468a107,
@@ -673,7 +690,8 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
},
}
@private _crc64_table_iso_3306 := [256]u16{
@(private, rodata)
_crc64_table_iso_3306 := [256]u16{
0x0000, 0x01b0, 0x0360, 0x02d0,
0x06c0, 0x0770, 0x05a0, 0x0410,
0x0d80, 0x0c30, 0x0ee0, 0x0f50,
@@ -739,3 +757,39 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
0x9480, 0x9530, 0x97e0, 0x9650,
0x9240, 0x93f0, 0x9120, 0x9090,
}
@(private, rodata)
_ccitt_0x1021_table := [256]u16{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
}

2
core/hash/doc.odin Normal file
View File

@@ -0,0 +1,2 @@
// `crc32`, `crc64`, `adler32`, `djb`, `fnv`, `jenkins`, `murmur` and other hashes.
package hash

View File

@@ -1,3 +1,8 @@
// Yann Collet's `xxhash`.
//
// [[ xxhash Fast Hash Algorithm; https://cyan4973.github.io/xxHash/ ]]
package xxhash
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
@@ -7,9 +12,6 @@
Jeroen van Rijn: Initial implementation.
*/
// An implementation of Yann Collet's [[ xxhash Fast Hash Algorithm; https://cyan4973.github.io/xxHash/ ]].
package xxhash
import "base:intrinsics"
import "base:runtime"

View File

@@ -1,3 +1,5 @@
package xxhash
/*
An implementation of Yann Collet's [xxhash Fast Hash Algorithm](https://cyan4973.github.io/xxHash/).
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
@@ -8,8 +10,6 @@
Jeroen van Rijn: Initial implementation.
*/
package xxhash
import "core:mem"
import "base:intrinsics"

View File

@@ -1,3 +1,5 @@
package xxhash
/*
An implementation of Yann Collet's [xxhash Fast Hash Algorithm](https://cyan4973.github.io/xxHash/).
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
@@ -8,8 +10,6 @@
Jeroen van Rijn: Initial implementation.
*/
package xxhash
import "base:intrinsics"
/*

View File

@@ -1,3 +1,5 @@
package xxhash
/*
An implementation of Yann Collet's [xxhash Fast Hash Algorithm](https://cyan4973.github.io/xxHash/).
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
@@ -8,8 +10,6 @@
Jeroen van Rijn: Initial implementation.
*/
package xxhash
import "base:intrinsics"
/*

View File

@@ -1,3 +1,5 @@
package xxhash
/*
An implementation of Yann Collet's [xxhash Fast Hash Algorithm](https://cyan4973.github.io/xxHash/).
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
@@ -8,8 +10,6 @@
Jeroen van Rijn: Initial implementation.
*/
package xxhash
import "base:intrinsics"
/*

View File

@@ -1,4 +1,4 @@
// package bmp implements a Microsoft BMP image reader
// Reader and writer for Microsoft `BMP` images.
package core_image_bmp
import "core:image"

View File

@@ -1,3 +1,5 @@
package image
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -7,9 +9,6 @@
Ginger Bill: Cosmetic changes.
*/
// package image implements a general 2D image library to be used with other image related packages
package image
import "core:bytes"
import "core:mem"
import "core:io"
@@ -64,8 +63,16 @@ Image_Metadata :: union #shared_nil {
^QOI_Info,
^TGA_Info,
^BMP_Info,
^JPEG_Info,
}
Exif :: struct {
byte_order: enum {
little_endian,
big_endian,
},
data: []u8 `fmt:"-"`,
}
/*
@@ -112,8 +119,7 @@ Image_Option:
`.alpha_drop_if_present`
If the image has an alpha channel, drop it.
You may want to use `.alpha_
tiply` in this case.
You may want to use `.alpha_premultiply` in this case.
NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
unless you select `alpha_premultiply`.
@@ -163,6 +169,7 @@ Error :: union #shared_nil {
PNG_Error,
QOI_Error,
BMP_Error,
JPEG_Error,
compress.Error,
compress.General_Error,
@@ -575,6 +582,140 @@ TGA_Info :: struct {
extension: Maybe(TGA_Extension),
}
/*
JPEG-specific
*/
JFIF_Magic := [?]byte{0x4A, 0x46, 0x49, 0x46} // "JFIF"
JFXX_Magic := [?]byte{0x4A, 0x46, 0x58, 0x58} // "JFXX"
Exif_Magic := [?]byte{0x45, 0x78, 0x69, 0x66} // "Exif"
JPEG_Error :: enum {
None = 0,
Duplicate_SOI_Marker,
Invalid_JFXX_Extension_Code,
Encountered_SOS_Before_SOF,
Invalid_Quantization_Table_Precision,
Invalid_Quantization_Table_Index,
Invalid_Huffman_Coefficient_Type,
Invalid_Huffman_Table_Index,
Unsupported_Frame_Type,
Invalid_Frame_Bit_Depth_Combo,
Invalid_Sampling_Factor,
Unsupported_12_Bit_Depth,
Multiple_SOS_Markers,
Encountered_RST_Marker_Outside_ECS,
Extra_Data_After_SOS, // Image seemed to have decoded okay, but there's more data after SOS
Invalid_Thumbnail_Size,
Huffman_Symbols_Exceeds_Max,
}
JFIF_Unit :: enum byte {
None = 0,
Dots_Per_Inch = 1,
Dots_Per_Centimeter = 2,
}
JFIF_APP0 :: struct {
version: u16be,
x_density: u16be,
y_density: u16be,
units: JFIF_Unit,
x_thumbnail: u8,
y_thumbnail: u8,
greyscale_thumbnail: bool,
thumbnail: []RGB_Pixel `fmt:"-"`,
}
JFXX_APP0 :: struct {
extension_code: JFXX_Extension_Code,
x_thumbnail: u8,
y_thumbnail: u8,
thumbnail: []byte `fmt:"-"`,
}
JFXX_Extension_Code :: enum u8 {
Thumbnail_JPEG = 0x10,
Thumbnail_1_Byte_Palette = 0x11,
Thumbnail_3_Byte_RGB = 0x13,
}
JPEG_Marker :: enum u8 {
SOF0 = 0xC0, // Baseline sequential DCT
SOF1 = 0xC1, // Extended sequential DCT
SOF2 = 0xC2, // Progressive DCT
SOF3 = 0xC3, // Lossless (sequential)
SOF5 = 0xC5, // Differential sequential DCT
SOF6 = 0xC6, // Differential progressive DCT
SOF7 = 0xC7, // Differential lossless (sequential)
SOF9 = 0xC9, // Extended sequential DCT, Arithmetic coding
SOF10 = 0xCA, // Progressive DCT, Arithmetic coding
SOF11 = 0xCB, // Lossless (sequential), Arithmetic coding
SOF13 = 0xCD, // Differential sequential DCT, Arithmetic coding
SOF14 = 0xCE, // Differential progressive DCT, Arithmetic coding
SOF15 = 0xCF, // Differential lossless (sequential), Arithmetic coding
DHT = 0xC4,
JPG = 0xC8,
DAC = 0xCC,
RST0 = 0xD0,
RST1 = 0xD1,
RST2 = 0xD2,
RST3 = 0xD3,
RST4 = 0xD4,
RST5 = 0xD5,
RST6 = 0xD6,
RST7 = 0xD7,
SOI = 0xD8,
EOI = 0xD9,
SOS = 0xDA,
DQT = 0xDB,
DNL = 0xDC,
DRI = 0xDD,
DHP = 0xDE,
EXP = 0xDF,
APP0 = 0xE0,
APP1 = 0xE1,
APP2 = 0xE2,
APP3 = 0xE3,
APP4 = 0xE4,
APP5 = 0xE5,
APP6 = 0xE6,
APP7 = 0xE7,
APP8 = 0xE8,
APP9 = 0xE9,
APP10 = 0xEA,
APP11 = 0xEB,
APP12 = 0xEC,
APP13 = 0xED,
APP14 = 0xEE,
APP15 = 0xEF,
JPG0 = 0xF0,
JPG1 = 0xF1,
JPG2 = 0xF2,
JPG3 = 0xF3,
JPG4 = 0xF4,
JPG5 = 0xF5,
JPG6 = 0xF6,
JPG7 = 0xF7,
JPG8 = 0xF8,
JPG9 = 0xF9,
JPG10 = 0xFA,
JPG11 = 0xFB,
JPG12 = 0xFC,
JPG13 = 0xFD,
COM = 0xFE,
TEM = 0x01,
}
JPEG_Info :: struct {
jfif_app0: Maybe(JFIF_APP0),
jfxx_app0: Maybe(JFXX_APP0),
comments: [dynamic]string,
exif: [dynamic]Exif,
frame_type: JPEG_Marker,
}
// Function to help with image buffer calculations
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height

2
core/image/doc.odin Normal file
View File

@@ -0,0 +1,2 @@
// General 2D image types and procedures to be used with other image related packages.
package image

View File

@@ -147,7 +147,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
return .JPEG
case s[:3] == "\xff\xd8\xff":
switch s[3] {
case 0xdb, 0xee, 0xe1, 0xe0:
case 0xdb, 0xee, 0xe1, 0xe0, 0xfe, 0xed:
return .JPEG
}
switch {

Some files were not shown because too many files have changed in this diff Show More