mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 04:50:29 +00:00
Merge branch 'master' into vendor/curl
This commit is contained in:
6
.github/workflows/nightly.yml
vendored
6
.github/workflows/nightly.yml
vendored
@@ -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
6
.gitignore
vendored
@@ -302,3 +302,9 @@ misc/featuregen/featuregen
|
||||
.cache/
|
||||
.clangd
|
||||
compile_commands.json
|
||||
|
||||
# Dev cmake helpers
|
||||
build/
|
||||
cmake-build*/
|
||||
CMakeLists.txt
|
||||
sandbox/
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,19 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
return true
|
||||
}
|
||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||
when ODIN_ARCH == .i386 && !ODIN_NO_CRT {
|
||||
// Windows i386 with CRT: libcmt provides mainCRTStartup which calls _main
|
||||
// Note: "c" calling convention adds underscore prefix automatically on i386
|
||||
@(link_name="main", linkage="strong", require)
|
||||
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
return 0
|
||||
}
|
||||
} else when ODIN_NO_CRT {
|
||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||
mainCRTStartup :: proc "system" () -> i32 {
|
||||
context = default_context()
|
||||
|
||||
@@ -71,10 +71,12 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return
|
||||
|
||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||
if zero_memory && new_size > old_size {
|
||||
new_region := raw_data(new_memory[old_size:])
|
||||
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||
when ODIN_OS != .Windows {
|
||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||
if zero_memory && new_size > old_size {
|
||||
new_region := raw_data(new_memory[old_size:])
|
||||
conditional_mem_zero(new_region, new_size - old_size)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ---
|
||||
}
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
comment: false
|
||||
comment: false
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 1%
|
||||
2
core/bufio/doc.odin
Normal file
2
core/bufio/doc.odin
Normal file
@@ -0,0 +1,2 @@
|
||||
// Wraps an `io.Stream` interface to provide buffered I/O.
|
||||
package bufio
|
||||
@@ -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
|
||||
|
||||
@@ -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' {
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
2
core/c/libc/doc.odin
Normal file
@@ -0,0 +1,2 @@
|
||||
// Declares the commonly used things in `libc` (`C` standard library).
|
||||
package libc
|
||||
@@ -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 ---
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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`.
|
||||
*/
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// A priority queue data structure.
|
||||
package container_priority_queue
|
||||
|
||||
import "base:builtin"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// A dynamically resizable double-ended queue/ring-buffer.
|
||||
package container_queue
|
||||
|
||||
import "base:builtin"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
package blake2b implements the BLAKE2b hash algorithm.
|
||||
`BLAKE2b` hash algorithm.
|
||||
|
||||
See:
|
||||
- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
package blake2s implements the BLAKE2s hash algorithm.
|
||||
`BLAKE2s` hash algorithm.
|
||||
|
||||
See:
|
||||
- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
package ed25519 implements the Ed25519 EdDSA signature algorithm.
|
||||
`Ed25519` EdDSA signature algorithm.
|
||||
|
||||
See:
|
||||
- [[ https://datatracker.ietf.org/doc/html/rfc8032 ]]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ]]
|
||||
*/
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ]]
|
||||
*/
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
package ristretto255 implement the ristretto255 prime-order group.
|
||||
Ristretto255 prime-order group.
|
||||
|
||||
See:
|
||||
- [[ https://www.rfc-editor.org/rfc/rfc9496 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -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 ]]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// A reader for the Windows `PE` executable format for debug purposes.
|
||||
package debug_pe
|
||||
|
||||
PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -44,4 +44,4 @@ main :: proc() {
|
||||
fmt.println("84 - 13 =", sym.sub(84, 13))
|
||||
fmt.println("hellope =", sym.hellope^)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Encoding and decoding of hex-encoded binary, e.g. `0x23` -> `#`.
|
||||
package encoding_hex
|
||||
|
||||
import "core:io"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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`.
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
2
core/hash/doc.odin
Normal file
@@ -0,0 +1,2 @@
|
||||
// `crc32`, `crc64`, `adler32`, `djb`, `fnv`, `jenkins`, `murmur` and other hashes.
|
||||
package hash
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
/*
|
||||
|
||||
@@ -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"
|
||||
|
||||
/*
|
||||
|
||||
@@ -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"
|
||||
|
||||
/*
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
2
core/image/doc.odin
Normal file
@@ -0,0 +1,2 @@
|
||||
// General 2D image types and procedures to be used with other image related packages.
|
||||
package image
|
||||
@@ -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
Reference in New Issue
Block a user