Merge branch 'odin-lang:master' into master

This commit is contained in:
WP. Yingamphol
2025-08-20 20:18:22 +07:00
committed by GitHub
191 changed files with 9672 additions and 2630 deletions

View File

@@ -47,20 +47,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jirutka/setup-alpine@v1
with:
branch: edge
- name: (Linux) Download LLVM
- name: (Linux) Download LLVM and Build Odin
run: |
apk add --no-cache \
musl-dev llvm20-dev clang20 git mold lz4 \
libxml2-static llvm20-static zlib-static zstd-static \
make
shell: alpine.sh --root {0}
- name: build odin
# NOTE: this build does slow compile times because of musl
run: ci/build_linux_static.sh
shell: alpine.sh {0}
docker run --rm -v "$PWD:/src" -w /src alpine sh -c '
apk add --no-cache \
musl-dev llvm20-dev clang20 git mold lz4 \
libxml2-static llvm20-static zlib-static zstd-static \
make &&
./ci/build_linux_static.sh
'
- name: Odin run
run: ./odin run examples/demo
- name: Copy artifacts
@@ -74,6 +69,7 @@ jobs:
cp -r core $FILE
cp -r vendor $FILE
cp -r examples $FILE
./ci/remove_windows_binaries.sh $FILE
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
tar -czvf dist.tar.gz $FILE
- name: Odin run
@@ -85,6 +81,46 @@ jobs:
with:
name: linux_artifacts
path: dist.tar.gz
build_linux_arm:
name: Linux ARM Build
if: github.repository == 'odin-lang/Odin'
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: (Linux ARM) Download LLVM and Build Odin
run: |
docker run --rm -v "$PWD:/src" -w /src arm64v8/alpine sh -c '
apk add --no-cache \
musl-dev llvm20-dev clang20 git mold lz4 \
libxml2-static llvm20-static zlib-static zstd-static \
make &&
./ci/build_linux_static.sh
'
- name: Odin run
run: ./odin run examples/demo
- name: Copy artifacts
run: |
FILE="odin-linux-arm64-nightly+$(date -I)"
mkdir $FILE
cp odin $FILE
cp LICENSE $FILE
cp -r shared $FILE
cp -r base $FILE
cp -r core $FILE
cp -r vendor $FILE
cp -r examples $FILE
./ci/remove_windows_binaries.sh $FILE
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
tar -czvf dist.tar.gz $FILE
- name: Odin run
run: |
FILE="odin-linux-arm64-nightly+$(date -I)"
$FILE/odin run examples/demo
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: linux_arm_artifacts
path: dist.tar.gz
build_macos:
name: MacOS Build
if: github.repository == 'odin-lang/Odin'
@@ -111,6 +147,7 @@ jobs:
cp -r core $FILE
cp -r vendor $FILE
cp -r examples $FILE
./ci/remove_windows_binaries.sh $FILE
dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
tar -czvf dist.tar.gz $FILE
@@ -149,6 +186,7 @@ jobs:
cp -r core $FILE
cp -r vendor $FILE
cp -r examples $FILE
./ci/remove_windows_binaries.sh $FILE
dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
tar -czvf dist.tar.gz $FILE
@@ -163,7 +201,7 @@ jobs:
path: dist.tar.gz
upload_b2:
runs-on: [ubuntu-latest]
needs: [build_windows, build_macos, build_macos_arm, build_linux]
needs: [build_windows, build_macos, build_macos_arm, build_linux, build_linux_arm]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@@ -192,6 +230,12 @@ jobs:
name: linux_artifacts
path: linux_artifacts
- name: Download Ubuntu ARM artifacts
uses: actions/download-artifact@v4.1.7
with:
name: linux_arm_artifacts
path: linux_arm_artifacts
- name: Download macOS artifacts
uses: actions/download-artifact@v4.1.7
with:
@@ -219,6 +263,7 @@ jobs:
file linux_artifacts/dist.tar.gz
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
python3 ci/nightly.py artifact linux-amd64 linux_artifacts/dist.tar.gz
python3 ci/nightly.py artifact linux-arm64 linux_arm_artifacts/dist.tar.gz
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.tar.gz
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.tar.gz
python3 ci/nightly.py prune

View File

@@ -145,7 +145,7 @@ ODIN_OS_STRING :: ODIN_OS_STRING
/*
An `enum` value indicating the platform subtarget, chosen using the `-subtarget` switch.
Possible values are: `.Default` `.iOS`, .iPhoneSimulator, and `.Android`.
Possible values are: `.Default` `.iPhone`, .iPhoneSimulator, and `.Android`.
*/
ODIN_PLATFORM_SUBTARGET :: ODIN_PLATFORM_SUBTARGET

View File

@@ -32,6 +32,7 @@ trap :: proc() -> ! ---
alloca :: proc(size, align: int) -> [^]u8 ---
cpu_relax :: proc() ---
read_cycle_counter :: proc() -> i64 ---
read_cycle_counter_frequency :: proc() -> i64 ---
count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
@@ -140,6 +141,7 @@ 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_string16 :: proc($T: typeid) -> bool ---
type_is_endian_platform :: proc($T: typeid) -> bool ---
type_is_endian_little :: proc($T: typeid) -> bool ---
@@ -231,6 +233,9 @@ type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), t
type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
// Returns the canonicalized name of the type, of which is used to produce the pseudo-unique 'typeid'
type_canonical_name :: proc($T: typeid) -> string ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---
@@ -314,6 +319,7 @@ simd_indices :: proc($T: typeid/#simd[$N]$E) -> T where type_is_numeric(T) ---
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
simd_runtime_swizzle :: proc(table: #simd[N]T, indices: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
// Lane-wise operations
simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
@@ -361,6 +367,7 @@ x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
// Darwin targets only
objc_object :: struct{}
objc_selector :: struct{}
@@ -377,6 +384,7 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
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) ---
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---

View File

@@ -61,6 +61,11 @@ Type_Info_Struct_Soa_Kind :: enum u8 {
Dynamic = 3,
}
Type_Info_String_Encoding_Kind :: enum u8 {
UTF_8 = 0,
UTF_16 = 1,
}
// Variant Types
Type_Info_Named :: struct {
name: string,
@@ -73,7 +78,7 @@ Type_Info_Rune :: struct {}
Type_Info_Float :: struct {endianness: Platform_Endianness}
Type_Info_Complex :: struct {}
Type_Info_Quaternion :: struct {}
Type_Info_String :: struct {is_cstring: bool}
Type_Info_String :: struct {is_cstring: bool, encoding: Type_Info_String_Encoding_Kind}
Type_Info_Boolean :: struct {}
Type_Info_Any :: struct {}
Type_Info_Type_Id :: struct {}
@@ -115,7 +120,7 @@ Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
Type_Info_Struct_Flag :: enum u8 {
packed = 0,
raw_union = 1,
no_copy = 2,
_ = 2,
align = 3,
}
@@ -397,6 +402,11 @@ Raw_String :: struct {
len: int,
}
Raw_String16 :: struct {
data: [^]u16,
len: int,
}
Raw_Slice :: struct {
data: rawptr,
len: int,
@@ -450,6 +460,12 @@ Raw_Cstring :: struct {
}
#assert(size_of(Raw_Cstring) == size_of(cstring))
Raw_Cstring16 :: struct {
data: [^]u16,
}
#assert(size_of(Raw_Cstring16) == size_of(cstring16))
Raw_Soa_Pointer :: struct {
data: rawptr,
index: int,
@@ -557,7 +573,7 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{
// Defined internally by the compiler
Odin_Platform_Subtarget_Type :: enum int {
Default,
iOS,
iPhone,
iPhoneSimulator
Android,
}
@@ -566,6 +582,8 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
Odin_Platform_Subtarget_Types :: bit_set[Odin_Platform_Subtarget_Type]
@(builtin)
ODIN_PLATFORM_SUBTARGET_IOS :: ODIN_PLATFORM_SUBTARGET == .iPhone || ODIN_PLATFORM_SUBTARGET == .iPhoneSimulator
/*
// Defined internally by the compiler

View File

@@ -5,6 +5,11 @@ import "base:intrinsics"
@builtin
Maybe :: union($T: typeid) {T}
/*
Represents an Objective-C block with a given procedure signature T
*/
@builtin
Objc_Block :: struct($T: typeid) where intrinsics.type_is_proc(T) { using _: intrinsics.objc_object }
/*
Recovers the containing/parent struct from a pointer to one of its fields.
@@ -86,11 +91,26 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
}
return n
}
// `copy_from_string16` 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_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` is a built-in procedure that copies elements from a source slice/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).
@builtin
copy :: proc{copy_slice, copy_from_string}
copy :: proc{copy_slice, copy_from_string, copy_from_string16}
@@ -285,6 +305,15 @@ delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error
}
@builtin
delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free_with_size(raw_data(str), len(str)*size_of(u16), allocator, loc)
}
@builtin
delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free((^u16)(str), allocator, loc)
}
// `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation.
//
// Note: Prefer `delete` over the specific `delete_*` procedures where possible.
@@ -297,6 +326,8 @@ delete :: proc{
delete_map,
delete_soa_slice,
delete_soa_dynamic_array,
delete_string16,
delete_cstring16,
}

View File

@@ -23,7 +23,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, .None
}
nil_allocator :: proc() -> Allocator {
nil_allocator :: proc "contextless" () -> Allocator {
return Allocator{
procedure = nil_allocator_proc,
data = nil,

View File

@@ -52,10 +52,13 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
return
}
memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) {
memory_block_dealloc :: proc "contextless" (block_to_free: ^Memory_Block, loc := #caller_location) {
if block_to_free != nil {
allocator := block_to_free.allocator
// sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity)
context = default_context()
context.allocator = allocator
mem_free(block_to_free, allocator, loc)
}
}
@@ -172,7 +175,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
arena.total_used = 0
}
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
arena_destroy :: proc "contextless" (arena: ^Arena, loc := #caller_location) {
for arena.curr_block != nil {
free_block := arena.curr_block
arena.curr_block = free_block.prev

View File

@@ -8,7 +8,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {}
default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {}
default_temp_allocator_proc :: nil_allocator_proc
@@ -28,7 +28,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
_ = arena_init(&s.arena, uint(size), backing_allocator)
}
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {
if s != nil {
arena_destroy(&s.arena)
s^ = {}
@@ -56,7 +56,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
}
@(fini, private)
_destroy_temp_allocator_fini :: proc() {
_destroy_temp_allocator_fini :: proc "contextless" () {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
}
}

244
base/runtime/doc.odin Normal file
View File

@@ -0,0 +1,244 @@
/*
Declarations which are required by the compiler
## Descriptions of files
There are a lot of files in this package and below is described roughly what
kind of functionality is placed in different files:
| File pattern | Description
|----------------------|------------------------------------------------------|
| `core.odin` | Contains the declarations that compiler will require to be present. Contains context-related declarations, `Type_Info` declarations and some other types used to implement the runtime and other packages. |
| `core_builtin*.odin` | Contain `@(builtin)` declarations that can be used without importing the package. Most of them aren't required by the compiler |
| `default_*.odin` | Contain default implementations for context allocators |
| `entry_*.odin` | Contain OS-specific entry points |
| `os_specific_*.odin` | Contain OS-specific utility procedures |
| `*internal*.odin` | Contain implementations for internal procedures that can be called by the compiler |
## Implementing custom runtime
For embedded and kernel development it might be required to re-implement parts
of the `base:runtime` package. This can include changing the default printing
procedures that handle console output when the program panics, custom
entry-points, tailored for a specific platform or execution environment, or
simply switching up implementations of some procedures.
In case this is required, the following is suggested:
1. Define `$ODIN_ROOT` environment variable to point to a directory within your
project that contains the following directories: `base/`, `core/` and `vendor/`.
2. Inside the `$ODIN_ROOT/base` subdirectory, implement the *necessary
declarations*.
What constitutes the necessary definitions is described below.
### Context-related
The compiler will require these declarations as they concern the `context`
variable.
* `Maybe`
* `Source_Code_Location`
* `Context`
* `Allocator`
* `Random_Generator`
* `Logger`
* `__init_context`
### Runtime initialization/cleanup
These are not strictly required for compilation, but if global variables or
`@(init)`/`@(fini)` blocks are used, these procedures need to be called inside
the entry point.
* `_startup_runtime`
* `_cleanup_runtime`
### Type assertion check
These procedures are called every time `.(Type)` expressions are used in order
to check the union tag or the underlying type of `any` before returning the
value of the underlying type. These are not required if `-no-type-assert` is
specified.
* `type_assertion_check`
* `type_assertion_check2` (takes in typeid)
### Bounds checking procedures
These procedures are called every time index or slicing expression are used in
order to perform bounds-checking before the actual operation. These are not
required if the `-no-bounds-check` option is specified.
* `bounds_check_error`
* `matrix_bounds_check_error`
* `slice_expr_error_hi`
* `slice_expr_error_lo_hi`
* `multi_pointer_slice_expr_error`
### cstring calls
If `cstring` or `cstring16` types are used, these procedures are required.
* `cstring_to_string`
* `cstring_len`
* `cstring16_to_string16`
* `cstring16_len`
### Comparison
These procedures are required for comparison operators between strings and other
compound types to function properly. If strings, structs nor unions are compared,
only `string_eq` procedure is required.
* `memory_equal`
* `memory_compare`
* `memory_compare_zero`
* `cstring_eq`
* `cstring16_eq`
* `cstring_ne`
* `cstring16_ne`
* `cstring_lt`
* `cstring16_lt`
* `cstring_gt`
* `cstring16_gt`
* `cstring_le`
* `cstring16_le`
* `cstring_ge`
* `cstring16_ge`
* `string_eq`
* `string16_eq`
* `string_ne`
* `string16_ne`
* `string_lt`
* `string16_lt`
* `string_gt`
* `string16_gt`
* `string_le`
* `string16_le`
* `string_ge`
* `string16_ge`
* `complex32_eq`
* `complex32_ne`
* `complex64_eq`
* `complex64_ne`
* `complex128_eq`
* `complex128_ne`
* `quaternion64_eq`
* `quaternion64_ne`
* `quaternion128_eq`
* `quaternion128_ne`
* `quaternion256_eq`
* `quaternion256_ne`
### for-in `string` type
These procedures are required to iterate strings using `for ... in` loop. If this
kind of loop isn't used, these procedures aren't required.
* `string_decode_rune`
* `string_decode_last_rune` (for `#reverse for`)
### Required when RTTI is enabled (the vast majority of targets)
These declarations are required unless the `-no-rtti` compiler option is
specified. Note that in order to be useful, some other procedures need to be
implemented. Those procedures aren't mentioned here as the compiler won't
complain if they're missing.
* `Type_Info`
* `type_table`
* `__type_info_of`
### Hashing
Required if maps are used
* `default_hasher`
* `default_hasher_cstring`
* `default_hasher_string`
### Pseudo-CRT required procedured due to LLVM but useful in general
* `memset`
* `memcpy`
* `memove`
### Procedures required by the LLVM backend if u128/i128 is used
* `umodti3`
* `udivti3`
* `modti3`
* `divti3`
* `fixdfti`
* `fixunsdfti`
* `fixunsdfdi`
* `floattidf`
* `floattidf_unsigned`
* `truncsfhf2`
* `truncdfhf2`
* `gnu_h2f_ieee`
* `gnu_f2h_ieee`
* `extendhfsf2`
### Procedures required by the LLVM backend if f16 is used (WASM only)
* `__ashlti3`
* `__multi3`
### When -no-crt is defined (windows only)
* `_tls_index`
* `_fltused`
### Arithmetic
* `quo_complex32`
* `quo_complex64`
* `quo_complex128`
* `mul_quaternion64`
* `mul_quaternion128`
* `mul_quaternion256`
* `quo_quaternion64`
* `quo_quaternion128`
* `quo_quaternion256`
* `abs_complex32`
* `abs_complex64`
* `abs_complex128`
* `abs_quaternion64`
* `abs_quaternion128`
* `abs_quaternion256`
## Map specific calls
* `map_seed_from_map_data`
* `__dynamic_map_check_grow` (for static map calls)
* `map_insert_hash_dynamic` (for static map calls)
* `__dynamic_map_get` (for dynamic map calls)
* `__dynamic_map_set` (for dynamic map calls)
## Dynamic literals (`[dynamic]T` and `map[K]V`) (can be disabled with `-no-dynamic-literals`)
* `__dynamic_array_reserve`
* `__dynamic_array_append`
* `__dynamic_map_reserve`
### Objective-C specific
* `objc_lookUpClass`
* `sel_registerName`
* `objc_allocateClassPair`
### Other required declarations
This is required without conditions.
* `Load_Directory_File`
*/
package runtime

View File

@@ -1,180 +0,0 @@
package runtime
/*
package runtime has numerous entities (declarations) which are required by the compiler to function.
## Basic types and calls (and anything they rely on)
Source_Code_Location
Context
Allocator
Logger
__init_context
_cleanup_runtime
## cstring calls
cstring_to_string
cstring_len
## Required when RTTI is enabled (the vast majority of targets)
Type_Info
type_table
__type_info_of
## Hashing
default_hasher
default_hasher_cstring
default_hasher_string
## Pseudo-CRT required procedured due to LLVM but useful in general
memset
memcpy
memove
## Procedures required by the LLVM backend if u128/i128 is used
umodti3
udivti3
modti3
divti3
fixdfti
fixunsdfti
fixunsdfdi
floattidf
floattidf_unsigned
truncsfhf2
truncdfhf2
gnu_h2f_ieee
gnu_f2h_ieee
extendhfsf2
## Procedures required by the LLVM backend if f16 is used
__ashlti3 // wasm specific
__multi3 // wasm specific
## Required an entry point is defined (i.e. 'main')
args__
## When -no-crt is defined (and not a wasm target) (mostly due to LLVM)
_tls_index
_fltused
## Bounds checking procedures (when not disabled with -no-bounds-check)
bounds_check_error
matrix_bounds_check_error
slice_expr_error_hi
slice_expr_error_lo_hi
multi_pointer_slice_expr_error
## Type assertion check
type_assertion_check
type_assertion_check2 // takes in typeid
## Arithmetic
quo_complex32
quo_complex64
quo_complex128
mul_quaternion64
mul_quaternion128
mul_quaternion256
quo_quaternion64
quo_quaternion128
quo_quaternion256
abs_complex32
abs_complex64
abs_complex128
abs_quaternion64
abs_quaternion128
abs_quaternion256
## Comparison
memory_equal
memory_compare
memory_compare_zero
cstring_eq
cstring_ne
cstring_lt
cstring_gt
cstring_le
cstring_gt
string_eq
string_ne
string_lt
string_gt
string_le
string_gt
complex32_eq
complex32_ne
complex64_eq
complex64_ne
complex128_eq
complex128_ne
quaternion64_eq
quaternion64_ne
quaternion128_eq
quaternion128_ne
quaternion256_eq
quaternion256_ne
## Map specific calls
map_seed_from_map_data
__dynamic_map_check_grow // static map calls
map_insert_hash_dynamic // static map calls
__dynamic_map_get // dynamic map calls
__dynamic_map_set // dynamic map calls
## Dynamic literals ([dynamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
__dynamic_array_reserve
__dynamic_array_append
__dynamic_map_reserve
## Objective-C specific
objc_lookUpClass
sel_registerName
objc_allocateClassPair
## for-in `string` type
string_decode_rune
string_decode_last_rune // #reverse for
*/

View File

@@ -3,6 +3,7 @@ bits 64
extern _start_odin
global _start
section .note.GNU-stack
section .text
;; Entry point for programs that specify -no-crt option
@@ -35,7 +36,7 @@ _start:
xor rbp, rbp
;; Load argc into 1st param reg, argv into 2nd param reg
pop rdi
mov rdx, rsi
mov rsi, rsp
;; Align stack pointer down to 16-bytes (sysv calling convention)
and rsp, -16
;; Call into odin entry point

View File

@@ -493,12 +493,40 @@ string_cmp :: proc "contextless" (a, b: string) -> int {
return ret
}
string16_eq :: proc "contextless" (lhs, rhs: string16) -> bool {
x := transmute(Raw_String16)lhs
y := transmute(Raw_String16)rhs
if x.len != y.len {
return false
}
return #force_inline memory_equal(x.data, y.data, x.len*size_of(u16))
}
string16_cmp :: proc "contextless" (a, b: string16) -> int {
x := transmute(Raw_String16)a
y := transmute(Raw_String16)b
ret := memory_compare(x.data, y.data, min(x.len, y.len)*size_of(u16))
if ret == 0 && x.len != y.len {
return -1 if x.len < y.len else +1
}
return ret
}
string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) }
string_lt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0 }
string_gt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0 }
string_le :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0 }
string_ge :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0 }
string16_ne :: #force_inline proc "contextless" (a, b: string16) -> bool { return !string16_eq(a, b) }
string16_lt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) < 0 }
string16_gt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) > 0 }
string16_le :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) <= 0 }
string16_ge :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) >= 0 }
cstring_len :: proc "contextless" (s: cstring) -> int {
p0 := uintptr((^byte)(s))
p := p0
@@ -508,6 +536,16 @@ cstring_len :: proc "contextless" (s: cstring) -> int {
return int(p - p0)
}
cstring16_len :: proc "contextless" (s: cstring16) -> int {
p := ([^]u16)(s)
n := 0
for p != nil && p[0] != 0 {
p = p[1:]
n += 1
}
return n
}
cstring_to_string :: proc "contextless" (s: cstring) -> string {
if s == nil {
return ""
@@ -517,6 +555,15 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string {
return transmute(string)Raw_String{ptr, n}
}
cstring16_to_string16 :: proc "contextless" (s: cstring16) -> string16 {
if s == nil {
return ""
}
ptr := (^u16)(s)
n := cstring16_len(s)
return transmute(string16)Raw_String16{ptr, n}
}
cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool {
x := ([^]byte)(lhs)
@@ -559,6 +606,46 @@ cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return
cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 }
cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 }
cstring16_eq :: proc "contextless" (lhs, rhs: cstring16) -> bool {
x := ([^]u16)(lhs)
y := ([^]u16)(rhs)
if x == y {
return true
}
if (x == nil) ~ (y == nil) {
return false
}
xn := cstring16_len(lhs)
yn := cstring16_len(rhs)
if xn != yn {
return false
}
return #force_inline memory_equal(x, y, xn*size_of(u16))
}
cstring16_cmp :: proc "contextless" (lhs, rhs: cstring16) -> int {
x := ([^]u16)(lhs)
y := ([^]u16)(rhs)
if x == y {
return 0
}
if (x == nil) ~ (y == nil) {
return -1 if x == nil else +1
}
xn := cstring16_len(lhs)
yn := cstring16_len(rhs)
ret := memory_compare(x, y, min(xn, yn)*size_of(u16))
if ret == 0 && xn != yn {
return -1 if xn < yn else +1
}
return ret
}
cstring16_ne :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return !cstring16_eq(a, b) }
cstring16_lt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) < 0 }
cstring16_gt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) > 0 }
cstring16_le :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) <= 0 }
cstring16_ge :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) >= 0 }
complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b) }
complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b) }
@@ -694,6 +781,68 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
return r, size
}
string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) {
REPLACEMENT_CHAR :: '\ufffd'
_surr1 :: 0xd800
_surr2 :: 0xdc00
_surr3 :: 0xe000
_surr_self :: 0x10000
r := rune(REPLACEMENT_CHAR)
if len(s) < 1 {
return r, 0
}
w := 1
switch c := s[0]; {
case c < _surr1, _surr3 <= c:
r = rune(c)
case _surr1 <= c && c < _surr2 && 1 < len(s) &&
_surr2 <= s[1] && s[1] < _surr3:
r1, r2 := rune(c), rune(s[1])
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
}
w += 1
}
return r, w
}
string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) {
REPLACEMENT_CHAR :: '\ufffd'
_surr1 :: 0xd800
_surr2 :: 0xdc00
_surr3 :: 0xe000
_surr_self :: 0x10000
r := rune(REPLACEMENT_CHAR)
if len(s) < 1 {
return r, 0
}
n := len(s)-1
c := s[n]
w := 1
if _surr2 <= c && c < _surr3 {
if n >= 1 {
r1 := rune(s[n-1])
r2 := rune(c)
if _surr1 <= r1 && r1 < _surr2 {
r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
}
w = 2
}
} else if c < _surr1 || _surr3 <= c {
r = rune(c)
}
return r, w
}
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
p, q := abs(real(x)), abs(imag(x))
if p < q {

View File

@@ -293,7 +293,14 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
print_string("quaternion")
print_u64(u64(8*ti.size))
case Type_Info_String:
if info.is_cstring {
print_byte('c')
}
print_string("string")
switch info.encoding {
case .UTF_8: /**/
case .UTF_16: print_string("16")
}
case Type_Info_Boolean:
switch ti.id {
case bool: print_string("bool")
@@ -403,7 +410,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
print_string("struct ")
if .packed in info.flags { print_string("#packed ") }
if .raw_union in info.flags { print_string("#raw_union ") }
if .no_copy in info.flags { print_string("#no_copy ") }
// if .no_copy in info.flags { print_string("#no_copy ") }
if .align in info.flags {
print_string("#align(")
print_u64(u64(ti.align))

View File

@@ -1,9 +1,12 @@
#+private
package runtime
@(priority_index=-1e6)
@(priority_index=-1e5)
foreign import ObjC "system:objc"
@(priority_index=-1e6)
foreign import libSystem "system:System"
import "base:intrinsics"
objc_id :: ^intrinsics.objc_object
@@ -31,5 +34,13 @@ foreign ObjC {
class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar ---
class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint ---
ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr ---
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
}
foreign libSystem {
_NSConcreteGlobalBlock: intrinsics.objc_class
_NSConcreteStackBlock: intrinsics.objc_class
_Block_object_assign :: proc "c" (rawptr, rawptr, i32) ---
_Block_object_dispose :: proc "c" (rawptr, i32) ---
}

View File

@@ -3,8 +3,8 @@ package runtime
init_default_context_for_js: Context
@(init, private="file")
init_default_context :: proc() {
init_default_context_for_js = context
init_default_context :: proc "contextless" () {
__init_context(&init_default_context_for_js)
}
@(export)

View File

@@ -97,7 +97,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
for &v in p {
if pos == 0 {
val = read_u64(r)
pos = 7
pos = 8
}
v = byte(val)
val >>= 8

View File

@@ -1,10 +1,14 @@
package runtime
Thread_Local_Cleaner :: #type proc "odin" ()
Thread_Local_Cleaner_Odin :: #type proc "odin" ()
Thread_Local_Cleaner_Contextless :: #type proc "contextless" ()
Thread_Local_Cleaner :: union #shared_nil {Thread_Local_Cleaner_Odin, Thread_Local_Cleaner_Contextless}
@(private="file")
thread_local_cleaners: [8]Thread_Local_Cleaner
// Add a procedure that will be run at the end of a thread for the purpose of
// deallocating state marked as `thread_local`.
//
@@ -29,6 +33,9 @@ run_thread_local_cleaners :: proc "odin" () {
if p == nil {
break
}
p()
switch v in p {
case Thread_Local_Cleaner_Odin: v()
case Thread_Local_Cleaner_Contextless: v()
}
}
}

8
ci/remove_windows_binaries.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env sh
find "$1" -type f \(\
-iname "*.exe" \
-o -iname "*.dll" \
-o -iname "*.lib" \
-o -iname "*.pdb" \
\) -delete

View File

@@ -72,14 +72,14 @@ when ODIN_OS == .Windows {
n_sep_by_space: c.char,
p_sign_posn: c.char,
n_sign_posn: c.char,
_W_decimal_point: [^]u16 `fmt:"s,0"`,
_W_thousands_sep: [^]u16 `fmt:"s,0"`,
_W_int_curr_symbol: [^]u16 `fmt:"s,0"`,
_W_currency_symbol: [^]u16 `fmt:"s,0"`,
_W_mon_decimal_point: [^]u16 `fmt:"s,0"`,
_W_mon_thousands_sep: [^]u16 `fmt:"s,0"`,
_W_positive_sign: [^]u16 `fmt:"s,0"`,
_W_negative_sign: [^]u16 `fmt:"s,0"`,
_W_decimal_point: cstring16,
_W_thousands_sep: cstring16,
_W_int_curr_symbol: cstring16,
_W_currency_symbol: cstring16,
_W_mon_decimal_point: cstring16,
_W_mon_thousands_sep: cstring16,
_W_positive_sign: cstring16,
_W_negative_sign: cstring16,
}
} else {
lconv :: struct {

View File

@@ -21,8 +21,7 @@ hash_string :: proc(algorithm: Algorithm, data: string, allocator := context.all
// in a newly allocated slice.
hash_bytes :: proc(algorithm: Algorithm, data: []byte, allocator := context.allocator) -> []byte {
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
hash_bytes_to_buffer(algorithm, data, dst)
return dst
return hash_bytes_to_buffer(algorithm, data, dst)
}
// hash_string_to_buffer will hash the given input and assign the
@@ -46,7 +45,7 @@ hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) -> []byte
update(&ctx, data)
final(&ctx, hash)
return hash
return hash[:DIGEST_SIZES[algorithm]]
}
// hash_stream will incrementally fully consume a stream, and return the

View File

@@ -54,7 +54,7 @@ _resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (
symbol.SizeOfStruct = size_of(symbol^)
symbol.MaxNameLen = 255
if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator)
fl.procedure, _ = win32.wstring_to_utf8(cstring16(&symbol.Name[0]), -1, allocator)
} else {
fl.procedure = fmt.aprintf("(procedure: 0x%x)", frame, allocator=allocator)
}

View File

@@ -13,7 +13,7 @@ _LIBRARY_FILE_EXTENSION :: "dll"
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
wide_path := win32.utf8_to_wstring(path, allocator)
defer free(wide_path, allocator)
defer free(rawptr(wide_path), allocator)
handle := cast(Library)win32.LoadLibraryW(wide_path)
return handle, handle != nil
}

View File

@@ -82,14 +82,16 @@ _tag_implementations_id: map[string]Tag_Implementation
_tag_implementations_type: map[typeid]Tag_Implementation
// Register a custom tag implementation to be used when marshalling that type and unmarshalling that tag number.
tag_register_type :: proc(impl: Tag_Implementation, nr: Tag_Number, type: typeid) {
tag_register_type :: proc "contextless" (impl: Tag_Implementation, nr: Tag_Number, type: typeid) {
context = runtime.default_context()
_tag_implementations_nr[nr] = impl
_tag_implementations_type[type] = impl
}
// Register a custom tag implementation to be used when marshalling that tag number or marshalling
// a field with the struct tag `cbor_tag:"nr"`.
tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string) {
tag_register_number :: proc "contextless" (impl: Tag_Implementation, nr: Tag_Number, id: string) {
context = runtime.default_context()
_tag_implementations_nr[nr] = impl
_tag_implementations_id[id] = impl
}
@@ -98,13 +100,13 @@ tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string
INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR)
@(private, init, disabled=!INITIALIZE_DEFAULT_TAGS)
tags_initialize_defaults :: proc() {
tags_initialize_defaults :: proc "contextless" () {
tags_register_defaults()
}
// Registers tags that have implementations provided by this package.
// This is done by default and can be controlled with the `CBOR_INITIALIZE_DEFAULT_TAGS` define.
tags_register_defaults :: proc() {
tags_register_defaults :: proc "contextless" () {
tag_register_number({nil, tag_time_unmarshal, tag_time_marshal}, TAG_EPOCH_TIME_NR, TAG_EPOCH_TIME_ID)
tag_register_number({nil, tag_base64_unmarshal, tag_base64_marshal}, TAG_BASE64_NR, TAG_BASE64_ID)
tag_register_number({nil, tag_cbor_unmarshal, tag_cbor_marshal}, TAG_CBOR_NR, TAG_CBOR_ID)
@@ -298,7 +300,7 @@ tag_base64_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number,
#partial switch t in ti.variant {
case reflect.Type_Info_String:
assert(t.encoding == .UTF_8)
if t.is_cstring {
length := base64.decoded_len(bytes)
builder := strings.builder_make(0, length+1)

View File

@@ -335,6 +335,8 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
#partial switch t in ti.variant {
case reflect.Type_Info_String:
assert(t.encoding == .UTF_8)
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
if t.is_cstring {

View File

@@ -353,10 +353,10 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
#partial switch info in ti.variant {
case runtime.Type_Info_String:
switch x in v {
case string:
return x == ""
case cstring:
return x == nil || x == ""
case string: return x == ""
case cstring: return x == nil || x == ""
case string16: return x == ""
case cstring16: return x == nil || x == ""
}
case runtime.Type_Info_Any:
return v.(any) == nil

View File

@@ -570,7 +570,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
key_ptr: rawptr
#partial switch tk in t.key.variant {
case runtime.Type_Info_String:
case runtime.Type_Info_String:
assert(tk.encoding == .UTF_8)
key_ptr = rawptr(&key)
key_cstr: cstring
if reflect.is_cstring(t.key) {

View File

@@ -127,6 +127,8 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
}
case runtime.Type_Info_String:
assert(specific_type_info.encoding == .UTF_8)
if specific_type_info.is_cstring {
cstr_ptr := (^cstring)(ptr)
if cstr_ptr != nil {

View File

@@ -38,6 +38,6 @@ Note that only one can be active at a time.
Inputs:
- setter: The type setter. Pass `nil` to disable any previously set setter.
*/
register_type_setter :: proc(setter: Custom_Type_Setter) {
register_type_setter :: proc "contextless" (setter: Custom_Type_Setter) {
global_custom_type_setter = setter
}

View File

@@ -1551,6 +1551,79 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) {
fmt_string(fi, string(s), verb)
}
// Formats a string UTF-16 with a specific format.
//
// Inputs:
// - fi: Pointer to the Info struct containing format settings.
// - s: The string to format.
// - verb: The format specifier character (e.g. 's', 'v', 'q', 'x', 'X').
//
fmt_string16 :: proc(fi: ^Info, s: string16, verb: rune) {
s, verb := s, verb
if ol, ok := fi.optional_len.?; ok {
s = s[:clamp(ol, 0, len(s))]
}
if !fi.in_bad && fi.record_level > 0 && verb == 'v' {
verb = 'q'
}
switch verb {
case 's', 'v':
if fi.width_set {
if fi.width > len(s) {
if fi.minus {
io.write_string16(fi.writer, s, &fi.n)
}
for _ in 0..<fi.width - len(s) {
io.write_byte(fi.writer, ' ', &fi.n)
}
if !fi.minus {
io.write_string16(fi.writer, s, &fi.n)
}
} else {
io.write_string16(fi.writer, s, &fi.n)
}
} else {
io.write_string16(fi.writer, s, &fi.n)
}
case 'q', 'w': // quoted string
io.write_quoted_string16(fi.writer, s, '"', &fi.n)
case 'x', 'X':
space := fi.space
fi.space = false
defer fi.space = space
for i in 0..<len(s) {
if i > 0 && space {
io.write_byte(fi.writer, ' ', &fi.n)
}
char_set := __DIGITS_UPPER
if verb == 'x' {
char_set = __DIGITS_LOWER
}
_fmt_int(fi, u64(s[i]), 16, false, bit_size=16, digits=char_set)
}
case:
fmt_bad_verb(fi, verb)
}
}
// Formats a C-style UTF-16 string with a specific format.
//
// Inputs:
// - fi: Pointer to the Info struct containing format settings.
// - s: The C-style string to format.
// - verb: The format specifier character (Ref fmt_string).
//
fmt_cstring16 :: proc(fi: ^Info, s: cstring16, verb: rune) {
fmt_string16(fi, string16(s), verb)
}
// Formats a raw pointer with a specific format.
//
// Inputs:
@@ -2273,14 +2346,14 @@ fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflec
}
switch reflect.type_info_base(elem).id {
case byte: fmt_string(fi, string(([^]byte)(data)[:n]), verb); return
case u16: print_utf16(fi, ([^]u16)(data)[:n]); return
case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return
case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return
case u32: print_utf32(fi, ([^]u32)(data)[:n]); return
case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return
case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return
case rune: print_utf32(fi, ([^]rune)(data)[:n]); return
case byte: fmt_string(fi, string (([^]byte)(data)[:n]), verb); return
case u16: fmt_string16(fi, string16(([^]u16) (data)[:n]), verb); return
case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return
case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return
case u32: print_utf32(fi, ([^]u32)(data)[:n]); return
case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return
case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return
case rune: print_utf32(fi, ([^]rune)(data)[:n]); return
}
}
if verb == 'p' {
@@ -3210,6 +3283,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case string: fmt_string(fi, a, verb)
case cstring: fmt_cstring(fi, a, verb)
case string16: fmt_string16(fi, a, verb)
case cstring16: fmt_cstring16(fi, a, verb)
case typeid: reflect.write_typeid(fi.writer, a, &fi.n)
case i16le: fmt_int(fi, u64(a), true, 16, verb)

View File

@@ -127,7 +127,7 @@ jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
}
@(optimization_mode="favor_size")
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
murmur32 :: proc "contextless" (data: []byte, seed := u32(0x9747b28c)) -> u32 {
c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593

View File

@@ -101,3 +101,35 @@ XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned)
return u64(b)
}
}
XXH64_read64_simd :: #force_inline proc(buf: []$E, $W: uint, alignment := Alignment.Unaligned) -> (res: #simd[W]u64) {
if alignment == .Aligned {
res = (^#simd[W]u64)(raw_data(buf))^
} else {
res = intrinsics.unaligned_load((^#simd[W]u64)(raw_data(buf)))
}
when ODIN_ENDIAN == .Big {
bytes := transmute(#simd[W*8]u8)res
bytes = intrinsics.simd_lanes_reverse(bytes)
res = transmute(#simd[W]u64)bytes
res = intrinsics.simd_lanes_reverse(res)
}
return
}
XXH64_write64_simd :: #force_inline proc(buf: []$E, value: $V/#simd[$W]u64, alignment := Alignment.Unaligned) {
value := value
when ODIN_ENDIAN == .Big {
bytes := transmute(#simd[W*8]u8)value
bytes = intrinsics.simd_lanes_reverse(bytes)
value = transmute(#simd[W]u64)bytes
value = intrinsics.simd_lanes_reverse(value)
}
if alignment == .Aligned {
(^V)(raw_data(buf))^ = value
} else {
intrinsics.unaligned_store((^V)(raw_data(buf)), value)
}
}

View File

@@ -52,6 +52,7 @@ XXH3_SECRET_SIZE_MIN :: 136
#assert(len(XXH3_kSecret) == 192 && len(XXH3_kSecret) > XXH3_SECRET_SIZE_MIN)
XXH_ACC_ALIGN :: 8 /* scalar */
XXH_MAX_WIDTH :: #config(XXH_MAX_WIDTH, 512) / 64
/*
This is the optimal update size for incremental hashing.
@@ -62,10 +63,11 @@ XXH3_INTERNAL_BUFFER_SIZE :: 256
Streaming state.
IMPORTANT: This structure has a strict alignment requirement of 64 bytes!! **
Do not allocate this with `make()` or `new`, it will not be sufficiently aligned.
Use`XXH3_create_state` and `XXH3_destroy_state, or stack allocation.
Default allocators will align it correctly if created via `new`, as will
placing this struct on the stack, but if using a custom allocator make sure
that it handles the alignment correctly!
*/
XXH3_state :: struct {
XXH3_state :: struct #align(64) {
acc: [8]u64,
custom_secret: [XXH_SECRET_DEFAULT_SIZE]u8,
buffer: [XXH3_INTERNAL_BUFFER_SIZE]u8,
@@ -380,7 +382,6 @@ XXH3_INIT_ACC :: [XXH_ACC_NB]xxh_u64{
XXH_SECRET_MERGEACCS_START :: 11
@(optimization_mode="favor_size")
XXH3_hashLong_128b_internal :: #force_inline proc(
input: []u8,
secret: []u8,
@@ -408,7 +409,6 @@ XXH3_hashLong_128b_internal :: #force_inline proc(
/*
* It's important for performance that XXH3_hashLong is not inlined.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
}
@@ -416,12 +416,10 @@ XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64,
/*
* It's important for performance that XXH3_hashLong is not inlined.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
}
@(optimization_mode="favor_size")
XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
input: []u8, seed: xxh_u64, secret: []u8,
f_acc512: XXH3_accumulate_512_f,
@@ -442,7 +440,6 @@ XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
/*
* It's important for performance that XXH3_hashLong is not inlined.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret)
}
@@ -477,7 +474,7 @@ XXH3_128bits_internal :: #force_inline proc(
/* === Public XXH128 API === */
@(optimization_mode="favor_size")
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_default)
}
@(optimization_mode="favor_size")
@@ -733,10 +730,6 @@ XXH3_accumulate_512_f :: #type proc(acc: []xxh_u64, input: []u8, secret:
XXH3_scramble_accumulator_f :: #type proc(acc: []xxh_u64, secret: []u8)
XXH3_init_custom_secret_f :: #type proc(custom_secret: []u8, seed64: xxh_u64)
XXH3_accumulate_512 : XXH3_accumulate_512_f = XXH3_accumulate_512_scalar
XXH3_scramble_accumulator : XXH3_scramble_accumulator_f = XXH3_scramble_accumulator_scalar
XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar
/* scalar variants - universal */
@(optimization_mode="favor_size")
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
@@ -751,7 +744,7 @@ XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, se
sec := XXH64_read64(xsecret[8 * i:])
data_key := data_val ~ sec
xacc[i ~ 1] += data_val /* swap adjacent lanes */
xacc[i ] += u64(u128(u32(data_key)) * u128(u64(data_key >> 32)))
xacc[i ] += u64(u32(data_key)) * u64(data_key >> 32)
}
}
@@ -785,6 +778,87 @@ XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64
}
}
/* generalized SIMD variants */
XXH3_accumulate_512_simd_generic :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8, $W: uint) {
u32xW :: #simd[W]u32
u64xW :: #simd[W]u64
#no_bounds_check for i in uint(0)..<XXH_ACC_NB/W {
data_val := XXH64_read64_simd(input[8 * W * i:], W)
sec := XXH64_read64_simd(secret[8 * W * i:], W)
data_key := data_val ~ sec
// Swap adjacent lanes
when W == 2 {
data_val = swizzle(data_val, 1, 0)
} else when W == 4 {
data_val = swizzle(data_val, 1, 0, 3, 2)
} else when W == 8 {
data_val = swizzle(data_val, 1, 0, 3, 2, 5, 4, 7, 6)
} else {
#panic("Unsupported vector size!")
}
a := XXH64_read64_simd(acc[W * i:], W)
a += data_val
a += u64xW(u32xW(data_key)) * intrinsics.simd_shr(data_key, 32)
XXH64_write64_simd(acc[W * i:], a)
}
}
XXH3_scramble_accumulator_simd_generic :: #force_inline proc(acc: []xxh_u64, secret: []u8, $W: uint) {
u64xW :: #simd[W]u64
#no_bounds_check for i in uint(0)..<XXH_ACC_NB/W {
key64 := XXH64_read64_simd(secret[8 * W * i:], W)
acc64 := XXH64_read64_simd(acc[W * i:], W)
acc64 ~= intrinsics.simd_shr(acc64, 47)
acc64 ~= key64
acc64 *= XXH_PRIME32_1
XXH64_write64_simd(acc[W * i:], acc64)
}
}
XXH3_init_custom_secret_simd_generic :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64, $W: uint) {
u64xW :: #simd[W]u64
seedVec := u64xW(seed64)
for i in 0..<W/2 {
j := 2*i + 1
seedVec = intrinsics.simd_replace(seedVec, j, -intrinsics.simd_extract(seedVec, j))
}
nbRounds := XXH_SECRET_DEFAULT_SIZE / 8 / W
#no_bounds_check for i in uint(0)..<nbRounds {
block := XXH64_read64_simd(XXH3_kSecret[8 * W * i:], W)
block += seedVec
XXH64_write64_simd(custom_secret[8 * W * i:], block)
}
}
XXH3_accumulate_512 :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
when XXH_NATIVE_WIDTH > 1 {
XXH3_accumulate_512_simd_generic(acc, input, secret, XXH_NATIVE_WIDTH)
} else {
XXH3_accumulate_512_scalar(acc, input, secret)
}
}
XXH3_scramble_accumulator :: #force_inline proc(acc: []xxh_u64, secret: []u8) {
when XXH_NATIVE_WIDTH > 1 {
XXH3_scramble_accumulator_simd_generic(acc, secret, XXH_NATIVE_WIDTH)
} else {
XXH3_scramble_accumulator_scalar(acc, secret)
}
}
XXH3_init_custom_secret :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) {
when XXH_NATIVE_WIDTH > 1 {
XXH3_init_custom_secret_simd_generic(custom_secret, seed64, XXH_NATIVE_WIDTH)
} else {
XXH3_init_custom_secret_scalar(custom_secret, seed64)
}
}
XXH_PREFETCH_DIST :: 320
/*
@@ -796,7 +870,7 @@ XXH_PREFETCH_DIST :: 320
XXH3_accumulate :: #force_inline proc(
acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) {
for n := uint(0); n < nbStripes; n += 1 {
#no_bounds_check for n := uint(0); n < nbStripes; n += 1 {
when !XXH_DISABLE_PREFETCH {
in_ptr := &input[n * XXH_STRIPE_LEN]
prefetch(in_ptr, XXH_PREFETCH_DIST)
@@ -869,7 +943,6 @@ XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
/*
It's important for performance that XXH3_hashLong is not inlined.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
}
@@ -881,24 +954,11 @@ XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u
This variant enforces that the compiler can detect that,
and uses this opportunity to streamline the generated code for better performance.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
}
/*
XXH3_hashLong_64b_withSeed():
Generate a custom key based on alteration of default XXH3_kSecret with the seed,
and then use this key for long mode hashing.
This operation is decently fast but nonetheless costs a little bit of time.
Try to avoid it whenever possible (typically when seed==0).
It's important for performance that XXH3_hashLong is not inlined. Not sure
why (uop cache maybe?), but the difference is large and easily measurable.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
XXH3_hashLong_64b_withSeed_internal :: #force_inline proc(
input: []u8,
seed: xxh_u64,
f_acc512: XXH3_accumulate_512_f,
@@ -915,9 +975,16 @@ XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
}
/*
It's important for performance that XXH3_hashLong is not inlined.
XXH3_hashLong_64b_withSeed():
Generate a custom key based on alteration of default XXH3_kSecret with the seed,
and then use this key for long mode hashing.
This operation is decently fast but nonetheless costs a little bit of time.
Try to avoid it whenever possible (typically when seed==0).
It's important for performance that XXH3_hashLong is not inlined. Not sure
why (uop cache maybe?), but the difference is large and easily measurable.
*/
@(optimization_mode="favor_size")
XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret)
}
@@ -926,7 +993,7 @@ XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64,
XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64)
@(optimization_mode="favor_size")
XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
XXH3_64bits_internal :: #force_inline proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
/*
If an action is to be taken if len(secret) condition is not respected, it should be done here.

View File

@@ -0,0 +1,13 @@
#+build amd64, i386
package xxhash
import "base:intrinsics"
@(private="file") SSE2_FEATURES :: "sse2"
@(private="file") AVX2_FEATURES :: "avx2"
@(private="file") AVX512_FEATURES :: "avx512dq,evex512"
XXH_NATIVE_WIDTH :: min(XXH_MAX_WIDTH,
8 when intrinsics.has_target_feature(AVX512_FEATURES) else
4 when intrinsics.has_target_feature(AVX2_FEATURES) else
2 when intrinsics.has_target_feature(SSE2_FEATURES) else 1)

View File

@@ -0,0 +1,8 @@
#+build !amd64
#+build !i386
package xxhash
import "base:runtime"
XXH_NATIVE_WIDTH :: min(XXH_MAX_WIDTH,
2 when runtime.HAS_HARDWARE_SIMD else 1)

View File

@@ -741,6 +741,6 @@ destroy :: proc(img: ^Image) {
}
@(init, private)
_register :: proc() {
_register :: proc "contextless" () {
image.register(.BMP, load_from_bytes, destroy)
}

View File

@@ -10,13 +10,13 @@ Destroy_Proc :: #type proc(img: ^Image)
_internal_loaders: [Which_File_Type]Loader_Proc
_internal_destroyers: [Which_File_Type]Destroy_Proc
register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
assert(loader != nil)
assert(destroyer != nil)
assert(_internal_loaders[kind] == nil)
register :: proc "contextless" (kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
assert_contextless(loader != nil)
assert_contextless(destroyer != nil)
assert_contextless(_internal_loaders[kind] == nil)
_internal_loaders[kind] = loader
assert(_internal_destroyers[kind] == nil)
assert_contextless(_internal_destroyers[kind] == nil)
_internal_destroyers[kind] = destroyer
}

View File

@@ -720,7 +720,7 @@ autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, for
}
@(init, private)
_register :: proc() {
_register :: proc "contextless" () {
loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) {
return load_from_bytes(data, allocator)
}

View File

@@ -1611,6 +1611,6 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
}
@(init, private)
_register :: proc() {
_register :: proc "contextless" () {
image.register(.PNG, load_from_bytes, destroy)
}

View File

@@ -371,6 +371,6 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) {
}
@(init, private)
_register :: proc() {
_register :: proc "contextless" () {
image.register(.QOI, load_from_bytes, destroy)
}

View File

@@ -406,6 +406,6 @@ IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4
IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5
@(init, private)
_register :: proc() {
_register :: proc "contextless" () {
image.register(.TGA, load_from_bytes, destroy)
}

View File

@@ -5,6 +5,7 @@ package io
import "base:intrinsics"
import "core:unicode/utf8"
import "core:unicode/utf16"
// Seek whence values
Seek_From :: enum {
@@ -314,6 +315,29 @@ write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int,
return write(s, transmute([]byte)str, n_written)
}
// write_string16 writes the contents of the string16 s to w reencoded as utf-8
write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: int, err: Error) {
for i := 0; i < len(str); i += 1 {
r := rune(utf16.REPLACEMENT_CHAR)
switch c := str[i]; {
case c < utf16._surr1, utf16._surr3 <= c:
r = rune(c)
case utf16._surr1 <= c && c < utf16._surr2 && i+1 < len(str) &&
utf16._surr2 <= str[i+1] && str[i+1] < utf16._surr3:
r = utf16.decode_surrogate_pair(rune(c), rune(str[i+1]))
i += 1
}
w: int
w, err = write_rune(s, r, n_written)
n += w
if err != nil {
return
}
}
return
}
// write_rune writes a UTF-8 encoded rune to w.
write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) {
defer if err == nil && n_written != nil {

View File

@@ -264,6 +264,33 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
return
}
write_quoted_string16 :: proc(w: Writer, str: string16, quote: byte = '"', n_written: ^int = nil, for_json := false) -> (n: int, err: Error) {
defer if n_written != nil {
n_written^ += n
}
write_byte(w, quote, &n) or_return
for width, s := 0, str; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RUNE_SELF {
r, width = utf16.decode_rune_in_string(s)
}
if width == 1 && r == utf8.RUNE_ERROR {
write_byte(w, '\\', &n) or_return
write_byte(w, 'x', &n) or_return
write_byte(w, DIGITS_LOWER[s[0]>>4], &n) or_return
write_byte(w, DIGITS_LOWER[s[0]&0xf], &n) or_return
continue
}
n_wrapper(write_escaped_rune(w, r, quote, false, nil, for_json), &n) or_return
}
write_byte(w, quote, &n) or_return
return
}
// writer append a quoted rune into the byte buffer, return the written size
write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) {
_write_byte :: #force_inline proc(w: Writer, c: byte) -> int {

View File

@@ -43,12 +43,14 @@ File_Console_Logger_Data :: struct {
@(private) global_subtract_stderr_options: Options
@(init, private)
init_standard_stream_status :: proc() {
init_standard_stream_status :: proc "contextless" () {
// NOTE(Feoramund): While it is technically possible for these streams to
// be redirected during the runtime of the program, the cost of checking on
// every single log message is not worth it to support such an
// uncommonly-used feature.
if terminal.color_enabled {
context = runtime.default_context()
// This is done this way because it's possible that only one of these
// streams could be redirected to a file.
if !terminal.is_terminal(os.stdout) {

View File

@@ -7,6 +7,7 @@
package math_big
import "base:intrinsics"
import "base:runtime"
import rnd "core:math/rand"
/*
@@ -778,22 +779,23 @@ int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, alloca
INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
@(init, private)
_init_constants :: proc() {
_init_constants :: proc "contextless" () {
initialize_constants()
}
initialize_constants :: proc() -> (res: int) {
internal_set( INT_ZERO, 0); INT_ZERO.flags = {.Immutable}
internal_set( INT_ONE, 1); INT_ONE.flags = {.Immutable}
internal_set(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable}
initialize_constants :: proc "contextless" () -> (res: int) {
context = runtime.default_context()
internal_int_set_from_integer( INT_ZERO, 0); INT_ZERO.flags = {.Immutable}
internal_int_set_from_integer( INT_ONE, 1); INT_ONE.flags = {.Immutable}
internal_int_set_from_integer(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable}
/*
We set these special values to -1 or 1 so they don't get mistake for zero accidentally.
This allows for shortcut tests of is_zero as .used == 0.
*/
internal_set( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN}
internal_set( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf}
internal_set(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
internal_int_set_from_integer( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN}
internal_int_set_from_integer( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf}
internal_int_set_from_integer(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
return _DEFAULT_MUL_KARATSUBA_CUTOFF
}

View File

@@ -27,10 +27,10 @@
package math_big
import "core:mem"
import "base:intrinsics"
import rnd "core:math/rand"
import "base:builtin"
import "base:intrinsics"
import "core:mem"
import rnd "core:math/rand"
/*
Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7.
@@ -2885,12 +2885,12 @@ internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context
}
internal_clear_if_uninitialized :: proc {internal_clear_if_uninitialized_single, internal_clear_if_uninitialized_multi, }
internal_error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) {
internal_error_if_immutable_single :: proc "contextless" (arg: ^Int) -> (err: Error) {
if arg != nil && .Immutable in arg.flags { return .Assignment_To_Immutable }
return nil
}
internal_error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) {
internal_error_if_immutable_multi :: proc "contextless" (args: ..^Int) -> (err: Error) {
for i in args {
if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable }
}

View File

@@ -56,13 +56,6 @@ query_info :: proc(gen := context.random_generator) -> Generator_Query_Info {
}
@(private)
_random_u64 :: proc(gen := context.random_generator) -> (res: u64) {
ok := runtime.random_generator_read_ptr(gen, &res, size_of(res))
assert(ok, "uninitialized gen/context.random_generator")
return
}
/*
Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -84,7 +77,7 @@ Possible Output:
*/
@(require_results)
uint32 :: proc(gen := context.random_generator) -> (val: u32) { return u32(_random_u64(gen)) }
uint32 :: proc(gen := context.random_generator) -> (val: u32) {return u32(uint64(gen))}
/*
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -107,7 +100,11 @@ Possible Output:
*/
@(require_results)
uint64 :: proc(gen := context.random_generator) -> (val: u64) { return _random_u64(gen) }
uint64 :: proc(gen := context.random_generator) -> (val: u64) {
ok := runtime.random_generator_read_ptr(gen, &val, size_of(val))
assert(ok, "uninitialized gen/context.random_generator")
return
}
/*
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -131,13 +128,13 @@ Possible Output:
*/
@(require_results)
uint128 :: proc(gen := context.random_generator) -> (val: u128) {
a := u128(_random_u64(gen))
b := u128(_random_u64(gen))
a := u128(uint64(gen))
b := u128(uint64(gen))
return (a<<64) | b
}
/*
Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
Returns:
@@ -160,7 +157,7 @@ Possible Output:
@(require_results) int31 :: proc(gen := context.random_generator) -> (val: i32) { return i32(uint32(gen) << 1 >> 1) }
/*
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
Returns:
@@ -183,7 +180,7 @@ Possible Output:
@(require_results) int63 :: proc(gen := context.random_generator) -> (val: i64) { return i64(uint64(gen) << 1 >> 1) }
/*
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
Returns:
@@ -480,8 +477,8 @@ Possible Output:
}
/*
Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
Due to floating point precision there is no guarantee if the upper and lower bounds are inclusive/exclusive with the exact floating point value.
Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
Due to floating point precision there is no guarantee if the upper and lower bounds are inclusive/exclusive with the exact floating point value.
Inputs:
- p: The byte slice to fill
@@ -508,22 +505,12 @@ Possible Output:
*/
@(require_results)
read :: proc(p: []byte, gen := context.random_generator) -> (n: int) {
pos := i8(0)
val := i64(0)
for n = 0; n < len(p); n += 1 {
if pos == 0 {
val = int63(gen)
pos = 7
}
p[n] = byte(val)
val >>= 8
pos -= 1
}
return
if !runtime.random_generator_read_bytes(gen, p) {return 0}
return len(p)
}
/*
Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
*Allocates Using Provided Allocator*
@@ -566,7 +553,7 @@ perm :: proc(n: int, allocator := context.allocator, gen := context.random_gener
}
/*
Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used.
Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used.
Inputs:
- array: The slice to randomize
@@ -607,7 +594,7 @@ shuffle :: proc(array: $T/[]$E, gen := context.random_generator) {
}
/*
Returns a random element from the provided slice. If no generator is provided the global random number generator will be used.
Returns a random element from the provided slice. If no generator is provided the global random number generator will be used.
Inputs:
- array: The slice to choose an element from

View File

@@ -2223,6 +2223,9 @@ Initialize a buddy allocator.
This procedure initializes the buddy allocator `b` with a backing buffer `data`
and block alignment specified by `alignment`.
`alignment` may be any power of two, but the backing buffer must be aligned to
at least `size_of(Buddy_Block)`.
*/
buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, loc := #caller_location) {
assert(data != nil)
@@ -2233,7 +2236,7 @@ buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint,
alignment = size_of(Buddy_Block)
}
ptr := raw_data(data)
assert(uintptr(ptr) % uintptr(alignment) == 0, "data is not aligned to minimum alignment", loc)
assert(uintptr(ptr) % uintptr(alignment) == 0, "The data is not aligned to the minimum alignment, which must be at least `size_of(Buddy_Block)`.", loc)
b.head = (^Buddy_Block)(ptr)
b.head.size = len(data)
b.head.is_free = true
@@ -2328,7 +2331,7 @@ buddy_allocator_alloc_bytes_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint)
}
found.is_free = false
data := ([^]byte)(found)[b.alignment:][:size]
assert(cast(uintptr)raw_data(data)+cast(uintptr)size < cast(uintptr)buddy_block_next(found), "Buddy_Allocator has made an allocation which overlaps a block header.")
assert(cast(uintptr)raw_data(data)+cast(uintptr)(size-1) < cast(uintptr)buddy_block_next(found), "Buddy_Allocator has made an allocation which overlaps a block header.")
// ensure_poisoned(data)
// sanitizer.address_unpoison(data)
return data, nil

View File

@@ -20,6 +20,17 @@ new_aligned :: proc(arena: ^Arena, $T: typeid, alignment: uint, loc := #caller_l
return
}
// The `new_clone` procedure allocates memory for a type `T` from a `virtual.Arena`. The second argument is a value that
// is to be copied to the allocated data. The value returned is a pointer to a newly allocated value of that type using the specified allocator.
@(require_results)
new_clone :: proc(arena: ^Arena, data: $T, loc := #caller_location) -> (ptr: ^T, err: Allocator_Error) {
ptr, err = new_aligned(arena, T, align_of(T), loc)
if ptr != nil && err == nil {
ptr^ = data
}
return
}
// `make_slice` allocates and initializes a slice. Like `new`, the second 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.
//

View File

@@ -9,7 +9,7 @@ _ :: runtime
DEFAULT_PAGE_SIZE := uint(4096)
@(init, private)
platform_memory_init :: proc() {
platform_memory_init :: proc "contextless" () {
_platform_memory_init()
}
@@ -89,8 +89,8 @@ memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags
reserved = align_formula(reserved, page_size)
committed = clamp(committed, 0, reserved)
total_size := uint(reserved + max(alignment, size_of(Platform_Memory_Block)))
base_offset := uintptr(max(alignment, size_of(Platform_Memory_Block)))
total_size := reserved + alignment + size_of(Platform_Memory_Block)
base_offset := mem.align_forward_uintptr(size_of(Platform_Memory_Block), max(uintptr(alignment), align_of(Platform_Memory_Block)))
protect_offset := uintptr(0)
do_protection := false

View File

@@ -43,10 +43,10 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags)
return errno == .NONE
}
_platform_memory_init :: proc() {
_platform_memory_init :: proc "contextless" () {
DEFAULT_PAGE_SIZE = 4096
// is power of two
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}

View File

@@ -25,7 +25,7 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags)
return false
}
_platform_memory_init :: proc() {
_platform_memory_init :: proc "contextless" () {
}
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {

View File

@@ -28,13 +28,13 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags)
return posix.mprotect(data, size, transmute(posix.Prot_Flags)flags) == .OK
}
_platform_memory_init :: proc() {
_platform_memory_init :: proc "contextless" () {
// NOTE: `posix.PAGESIZE` due to legacy reasons could be wrong so we use `sysconf`.
size := posix.sysconf(._PAGESIZE)
DEFAULT_PAGE_SIZE = uint(max(size, posix.PAGESIZE))
// is power of two
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {

View File

@@ -72,7 +72,7 @@ foreign Kernel32 {
flProtect: u32,
dwMaximumSizeHigh: u32,
dwMaximumSizeLow: u32,
lpName: [^]u16,
lpName: cstring16,
) -> rawptr ---
MapViewOfFile :: proc(
@@ -146,13 +146,13 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags)
@(no_sanitize_address)
_platform_memory_init :: proc() {
_platform_memory_init :: proc "contextless" () {
sys_info: SYSTEM_INFO
GetSystemInfo(&sys_info)
DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, uint(sys_info.dwPageSize))
// is power of two
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}

View File

@@ -79,7 +79,7 @@ Shutdown_Manner :: enum c.int {
}
@(init, private)
ensure_winsock_initialized :: proc() {
ensure_winsock_initialized :: proc "contextless" () {
win.ensure_winsock_initialized()
}

View File

@@ -209,14 +209,14 @@ scan_comment :: proc(t: ^Tokenizer) -> string {
scan_file_tag :: proc(t: ^Tokenizer) -> string {
offset := t.offset - 1
for t.ch != '\n' {
for t.ch != '\n' && t.ch != utf8.RUNE_EOF {
if t.ch == '/' {
next := peek_byte(t, 0)
if next == '/' || next == '*' {
break
}
}
}
advance_rune(t)
}

View File

@@ -87,7 +87,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE {
err = get_last_error()
return dfi[:], err

View File

@@ -16,7 +16,7 @@ MAX_TEMP_ARENA_COLLISIONS :: MAX_TEMP_ARENA_COUNT - 1
global_default_temp_allocator_arenas: [MAX_TEMP_ARENA_COUNT]runtime.Arena
@(fini, private)
temp_allocator_fini :: proc() {
temp_allocator_fini :: proc "contextless" () {
for &arena in global_default_temp_allocator_arenas {
runtime.arena_destroy(&arena)
}
@@ -69,6 +69,6 @@ _temp_allocator_end :: proc(tmp: runtime.Arena_Temp) {
}
@(init, private)
init_thread_local_cleaner :: proc() {
init_thread_local_cleaner :: proc "contextless" () {
runtime.add_thread_local_cleaner(temp_allocator_fini)
}

View File

@@ -16,7 +16,7 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
path := concatenate({base_path, `\`, win32_wstring_to_utf8(raw_data(d.cFileName[:]), temp_allocator) or_else ""}, allocator) or_return
path := concatenate({base_path, `\`, win32_wstring_to_utf8(cstring16(raw_data(d.cFileName[:])), temp_allocator) or_else ""}, allocator) or_return
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
defer win32.CloseHandle(handle)
@@ -107,15 +107,7 @@ _read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
return
}
wpath: []u16
{
i := 0
for impl.wname[i] != 0 {
i += 1
}
wpath = impl.wname[:i]
}
wpath := string16(impl.wname)
temp_allocator := TEMP_ALLOCATOR_GUARD({})
wpath_search := make([]u16, len(wpath)+3, temp_allocator)
@@ -124,7 +116,7 @@ _read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
wpath_search[len(wpath)+1] = '*'
wpath_search[len(wpath)+2] = 0
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
it.impl.find_handle = win32.FindFirstFileW(cstring16(raw_data(wpath_search)), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
read_directory_iterator_set_error(it, impl.name, _get_platform_error())
return

View File

@@ -31,7 +31,7 @@ _lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value:
return "", false
}
value = win32_utf16_to_utf8(b[:n], allocator) or_else ""
value = win32_utf16_to_utf8(string16(b[:n]), allocator) or_else ""
found = true
return
}

View File

@@ -45,8 +45,8 @@ _stderr := File{
}
@init
_standard_stream_init :: proc() {
new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
_standard_stream_init :: proc "contextless" () {
new_std :: proc "contextless" (impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
impl.file.impl = impl
impl.fd = linux.Fd(fd)
impl.allocator = runtime.nil_allocator()

View File

@@ -25,8 +25,8 @@ File_Impl :: struct {
}
@(init)
init_std_files :: proc() {
new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
init_std_files :: proc "contextless" () {
new_std :: proc "contextless" (impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
impl.file.impl = impl
impl.fd = fd
impl.allocator = runtime.nil_allocator()

View File

@@ -30,8 +30,8 @@ Preopen :: struct {
preopens: []Preopen
@(init)
init_std_files :: proc() {
new_std :: proc(impl: ^File_Impl, fd: wasi.fd_t, name: string) -> ^File {
init_std_files :: proc "contextless" () {
new_std :: proc "contextless" (impl: ^File_Impl, fd: wasi.fd_t, name: string) -> ^File {
impl.file.impl = impl
impl.allocator = runtime.nil_allocator()
impl.fd = fd

View File

@@ -43,8 +43,8 @@ File_Impl :: struct {
}
@(init)
init_std_files :: proc() {
new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
init_std_files :: proc "contextless" () {
new_std :: proc "contextless" (impl: ^File_Impl, code: u32, name: string) -> ^File {
impl.file.impl = impl
impl.allocator = runtime.nil_allocator()
@@ -77,7 +77,7 @@ init_std_files :: proc() {
stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "<stderr>")
}
_handle :: proc(f: ^File) -> win32.HANDLE {
_handle :: proc "contextless" (f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f))
}
@@ -234,7 +234,7 @@ _clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
return _new_file(uintptr(clonefd), name(f), file_allocator())
}
_fd :: proc(f: ^File) -> uintptr {
_fd :: proc "contextless" (f: ^File) -> uintptr {
if f == nil || f.impl == nil {
return INVALID_HANDLE
}
@@ -247,11 +247,11 @@ _destroy :: proc(f: ^File_Impl) -> Error {
}
a := f.allocator
err0 := free(f.wname, a)
err0 := free(rawptr(f.wname), a)
err1 := delete(f.name, a)
err2 := free(f, a)
err3 := delete(f.r_buf, a)
err4 := delete(f.w_buf, a)
err2 := delete(f.r_buf, a)
err3 := delete(f.w_buf, a)
err4 := free(f, a)
err0 or_return
err1 or_return
err2 or_return
@@ -619,7 +619,7 @@ _symlink :: proc(old_name, new_name: string) -> Error {
return .Unsupported
}
_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
_open_sym_link :: proc(p: cstring16) -> (handle: win32.HANDLE, err: Error) {
attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
@@ -661,7 +661,7 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st
}
handle := _open_sym_link(raw_data(p)) or_return
handle := _open_sym_link(cstring16(raw_data(p))) or_return
defer win32.CloseHandle(handle)
n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
@@ -672,7 +672,7 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
buf := make([]u16, n+1, temp_allocator)
n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
n = win32.GetFinalPathNameByHandleW(handle, cstring16(raw_data(buf)), u32(len(buf)), win32.VOLUME_NAME_DOS)
if n == 0 {
return "", _get_platform_error()
}
@@ -713,7 +713,7 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er
switch rdb.ReparseTag {
case win32.IO_REPARSE_TAG_SYMLINK:
rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
pb := win32.wstring(&rb.PathBuffer)
pb := ([^]u16)(&rb.PathBuffer)
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
@@ -723,7 +723,7 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er
case win32.IO_REPARSE_TAG_MOUNT_POINT:
rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
pb := win32.wstring(&rb.PathBuffer)
pb := ([^]u16)(&rb.PathBuffer)
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
return _normalize_link_path(p, allocator)
@@ -874,8 +874,8 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
@(private="package", require_results)
win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: [^]u16, err: runtime.Allocator_Error) {
ws = raw_data(win32_utf8_to_utf16(s, allocator) or_return)
win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: cstring16, err: runtime.Allocator_Error) {
ws = cstring16(raw_data(win32_utf8_to_utf16(s, allocator) or_return))
return
}
@@ -909,24 +909,26 @@ win32_utf8_to_utf16 :: proc(s: string, allocator: runtime.Allocator) -> (ws: []u
}
@(private="package", require_results)
win32_wstring_to_utf8 :: proc(s: [^]u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
if s == nil || s[0] == 0 {
win32_wstring_to_utf8 :: proc(s: cstring16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
if s == nil || s == "" {
return "", nil
}
n := 0
for s[n] != 0 {
n += 1
}
return win32_utf16_to_utf8(s[:n], allocator)
return win32_utf16_to_utf8(string16(s), allocator)
}
@(private="package")
win32_utf16_to_utf8 :: proc{
win32_utf16_string16_to_utf8,
win32_utf16_u16_to_utf8,
}
@(private="package", require_results)
win32_utf16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
win32_utf16_string16_to_utf8 :: proc(s: string16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
if len(s) == 0 {
return
}
n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), nil, 0, nil, nil)
n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), nil, 0, nil, nil)
if n == 0 {
return
}
@@ -938,7 +940,41 @@ win32_utf16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: str
// will not be null terminated.
text := make([]byte, n, allocator) or_return
n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), raw_data(text), n, nil, nil)
n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), raw_data(text), n, nil, nil)
if n1 == 0 {
delete(text, allocator)
return
}
for i in 0..<n {
if text[i] == 0 {
n = i
break
}
}
res = string(text[:n])
return
}
@(private="package", require_results)
win32_utf16_u16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
if len(s) == 0 {
return
}
n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), nil, 0, nil, nil)
if n == 0 {
return
}
// If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
// and will scan it to find the first null terminated character. The resulting string will
// also be null terminated.
// If N > 0 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated.
text := make([]byte, n, allocator) or_return
n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), raw_data(text), n, nil, nil)
if n1 == 0 {
delete(text, allocator)
return

View File

@@ -91,11 +91,11 @@ _remove_all :: proc(path: string) -> Error {
nil,
win32.FO_DELETE,
dir,
&empty[0],
cstring16(&empty[0]),
win32.FOF_NOCONFIRMATION | win32.FOF_NOERRORUI | win32.FOF_SILENT,
false,
nil,
&empty[0],
cstring16(&empty[0]),
}
res := win32.SHFileOperationW(&file_op)
if res != 0 {
@@ -160,7 +160,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err
can_use_long_paths: bool
@(init)
init_long_path_support :: proc() {
init_long_path_support :: proc "contextless" () {
can_use_long_paths = false
key: win32.HKEY
@@ -303,13 +303,13 @@ _get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absol
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
rel_utf16 := win32.utf8_to_utf16(rel, temp_allocator)
n := win32.GetFullPathNameW(raw_data(rel_utf16), 0, nil, nil)
n := win32.GetFullPathNameW(cstring16(raw_data(rel_utf16)), 0, nil, nil)
if n == 0 {
return "", Platform_Error(win32.GetLastError())
}
buf := make([]u16, n, temp_allocator) or_return
n = win32.GetFullPathNameW(raw_data(rel_utf16), u32(n), raw_data(buf), nil)
n = win32.GetFullPathNameW(cstring16(raw_data(rel_utf16)), u32(n), cstring16(raw_data(buf)), nil)
if n == 0 {
return "", Platform_Error(win32.GetLastError())
}

View File

@@ -16,7 +16,8 @@ Arguments to the current process.
args := get_args()
@(private="file")
get_args :: proc() -> []string {
get_args :: proc "contextless" () -> []string {
context = runtime.default_context()
result := make([]string, len(runtime.args__), heap_allocator())
for rt_arg, i in runtime.args__ {
result[i] = string(rt_arg)
@@ -24,6 +25,12 @@ get_args :: proc() -> []string {
return result
}
@(fini, private="file")
delete_args :: proc "contextless" () {
context = runtime.default_context()
delete(args, heap_allocator())
}
/*
Exit the current process.
*/

View File

@@ -13,7 +13,7 @@ import "core:time"
foreign import lib "system:System"
foreign lib {
sysctl :: proc(
sysctl :: proc "c" (
name: [^]i32, namelen: u32,
oldp: rawptr, oldlenp: ^uint,
newp: rawptr, newlen: uint,

View File

@@ -175,7 +175,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
info.fields += {.Command_Line}
}
if .Command_Args in selection {
info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return
info.command_args = _parse_command_line(cstring16(raw_data(cmdline_w)), allocator) or_return
info.fields += {.Command_Args}
}
}
@@ -286,7 +286,7 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
info.fields += {.Command_Line}
}
if .Command_Args in selection {
info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return
info.command_args = _parse_command_line(cstring16(raw_data(cmdline_w)), allocator) or_return
info.fields += {.Command_Args}
}
}
@@ -610,7 +610,7 @@ _process_exe_by_pid :: proc(pid: int, allocator: runtime.Allocator) -> (exe_path
err =_get_platform_error()
return
}
return win32_wstring_to_utf8(raw_data(entry.szExePath[:]), allocator)
return win32_wstring_to_utf8(cstring16(raw_data(entry.szExePath[:])), allocator)
}
_get_process_user :: proc(process_handle: win32.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) {
@@ -650,7 +650,7 @@ _get_process_user :: proc(process_handle: win32.HANDLE, allocator: runtime.Alloc
return strings.concatenate({domain, "\\", username}, allocator)
}
_parse_command_line :: proc(cmd_line_w: [^]u16, allocator: runtime.Allocator) -> (argv: []string, err: Error) {
_parse_command_line :: proc(cmd_line_w: cstring16, allocator: runtime.Allocator) -> (argv: []string, err: Error) {
argc: i32
argv_w := win32.CommandLineToArgvW(cmd_line_w, &argc)
if argv_w == nil {

View File

@@ -49,12 +49,12 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
p := win32_utf8_to_utf16(name, temp_allocator) or_return
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil)
if n == 0 {
return "", _get_platform_error()
}
buf := make([]u16, n+1, temp_allocator)
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
if n == 0 {
return "", _get_platform_error()
}
@@ -140,8 +140,8 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
buf := make([]u16, max(n, 260)+1, temp_allocator)
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
return _cleanpath_from_buf(buf[:n], allocator)
n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0)
return _cleanpath_from_buf(string16(buf[:n]), allocator)
}
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
@@ -158,12 +158,12 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
buf := make([]u16, max(n, 260)+1, temp_allocator)
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0)
return _cleanpath_strip_prefix(buf[:n]), nil
}
_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
buf := buf
_cleanpath_from_buf :: proc(buf: string16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
buf := transmute([]u16)buf
buf = _cleanpath_strip_prefix(buf)
return win32_utf16_to_utf8(buf, allocator)
}

View File

@@ -12,12 +12,12 @@ _temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Er
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
b := make([]u16, max(win32.MAX_PATH, n), temp_allocator)
n = win32.GetTempPathW(u32(len(b)), raw_data(b))
n = win32.GetTempPathW(u32(len(b)), cstring16(raw_data(b)))
if n == 3 && b[1] == ':' && b[2] == '\\' {
} else if n > 0 && b[n-1] == '\\' {
n -= 1
}
return win32_utf16_to_utf8(b[:n], allocator)
return win32_utf16_to_utf8(string16(b[:n]), allocator)
}

View File

@@ -74,6 +74,5 @@ _get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.
return "", .Invalid_Path
}
dir, _ = win32.wstring_to_utf8(path_w, -1, allocator)
return
return win32_wstring_to_utf8(cstring16(path_w), allocator)
}

View File

@@ -1226,7 +1226,8 @@ _processor_core_count :: proc() -> int {
}
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
res := make([]string, len(runtime.args__))
for _, i in res {
res[i] = string(runtime.args__[i])
@@ -1235,7 +1236,8 @@ _alloc_command_line_arguments :: proc() -> []string {
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}

View File

@@ -965,15 +965,17 @@ _processor_core_count :: proc() -> int {
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
res[i] = string(arg)
for _, i in res {
res[i] = string(runtime.args__[i])
}
return res
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}

View File

@@ -317,7 +317,8 @@ file_size :: proc(fd: Handle) -> (i64, Error) {
args := _alloc_command_line_arguments()
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
res[i] = string(arg)
@@ -326,7 +327,8 @@ _alloc_command_line_arguments :: proc() -> []string {
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}

View File

@@ -1098,16 +1098,18 @@ _processor_core_count :: proc() -> int {
}
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
res[i] = string(arg)
for _, i in res {
res[i] = string(runtime.args__[i])
}
return res
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}

View File

@@ -1015,15 +1015,17 @@ _processor_core_count :: proc() -> int {
}
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
res[i] = string(arg)
for _, i in res {
res[i] = string(runtime.args__[i])
}
return res
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}

View File

@@ -915,15 +915,17 @@ _processor_core_count :: proc() -> int {
}
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
res[i] = string(arg)
for _, i in res {
res[i] = string(runtime.args__[i])
}
return res
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}

View File

@@ -28,16 +28,18 @@ stderr: Handle = 2
args := _alloc_command_line_arguments()
@(private, require_results)
_alloc_command_line_arguments :: proc() -> (args: []string) {
args = make([]string, len(runtime.args__))
for &arg, i in args {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
cmd_args := make([]string, len(runtime.args__))
for &arg, i in cmd_args {
arg = string(runtime.args__[i])
}
return
return cmd_args
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
delete(args)
}
@@ -57,9 +59,8 @@ Preopen :: struct {
preopens: []Preopen
@(init, private)
init_preopens :: proc() {
strip_prefixes :: proc(path: string) -> string {
init_preopens :: proc "contextless" () {
strip_prefixes :: proc "contextless"(path: string) -> string {
path := path
loop: for len(path) > 0 {
switch {
@@ -76,6 +77,8 @@ init_preopens :: proc() {
return path
}
context = runtime.default_context()
dyn_preopens: [dynamic]Preopen
loop: for fd := wasi.fd_t(3); ; fd += 1 {
desc, err := wasi.fd_prestat_get(fd)

View File

@@ -194,7 +194,8 @@ current_thread_id :: proc "contextless" () -> int {
@(private, require_results)
_alloc_command_line_arguments :: proc() -> []string {
_alloc_command_line_arguments :: proc "contextless" () -> []string {
context = runtime.default_context()
arg_count: i32
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count)
arg_list := make([]string, int(arg_count))
@@ -216,7 +217,8 @@ _alloc_command_line_arguments :: proc() -> []string {
}
@(private, fini)
_delete_command_line_arguments :: proc() {
_delete_command_line_arguments :: proc "contextless" () {
context = runtime.default_context()
for s in args {
delete(s)
}

View File

@@ -17,7 +17,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
buf := make([dynamic]u16, 100)
defer delete(buf)
for {
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
if n == 0 {
return "", get_last_error()
}
@@ -154,7 +154,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> (
return nil, get_last_error()
}
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0)
return buf[:buf_len], nil
}
@(private, require_results)

View File

@@ -1,4 +1,5 @@
#+build !wasi
#+build !js
package filepath
import "core:os"

View File

@@ -61,13 +61,13 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Error) {
}
p := win32.utf8_to_utf16(name, ta)
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil)
if n == 0 {
return "", os.get_last_error()
}
buf := make([]u16, n, ta)
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
if n == 0 {
delete(buf)
return "", os.get_last_error()

View File

@@ -1,4 +1,5 @@
#+build !wasi
#+build !js
package filepath
import "core:os"

View File

@@ -8,29 +8,36 @@ import "base:intrinsics"
MANUAL_MAGIC :: u64le(0x0BADF00D)
Manual_Header :: struct #packed {
Manual_Stream_Header :: struct #packed {
magic: u64le,
version: u64le,
timestamp_scale: f64le,
reserved: u64le,
}
Manual_Buffer_Header :: struct #packed {
size: u32le,
tid: u32le,
pid: u32le,
first_ts: u64le,
}
Manual_Event_Type :: enum u8 {
Invalid = 0,
Invalid = 0,
Begin = 3,
End = 4,
Instant = 5,
Begin = 3,
End = 4,
Instant = 5,
Pad_Skip = 7,
Pad_Skip = 7,
Name_Process = 8,
Name_Thread = 9,
}
Begin_Event :: struct #packed {
type: Manual_Event_Type,
category: u8,
pid: u32le,
tid: u32le,
ts: f64le,
ts: u64le,
name_len: u8,
args_len: u8,
}
@@ -38,9 +45,7 @@ BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255
End_Event :: struct #packed {
type: Manual_Event_Type,
pid: u32le,
tid: u32le,
ts: f64le,
ts: u64le,
}
Pad_Skip :: struct #packed {
@@ -48,6 +53,12 @@ Pad_Skip :: struct #packed {
size: u32le,
}
Name_Event :: struct #packed {
type: Manual_Event_Type,
name_len: u8,
}
NAME_EVENT_MAX :: size_of(Name_Event) + 255
// User Interface
Context :: struct {
@@ -61,6 +72,7 @@ Buffer :: struct {
head: int,
tid: u32,
pid: u32,
first_ts: u64,
}
BUFFER_DEFAULT_SIZE :: 0x10_0000
@@ -76,8 +88,8 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam
ctx.precise_time = precise_time
ctx.timestamp_scale = timestamp_scale
temp := [size_of(Manual_Header)]u8{}
_build_header(temp[:], ctx.timestamp_scale)
temp := [size_of(Manual_Stream_Header)]u8{}
_build_stream_header(temp[:], ctx.timestamp_scale)
os.write(ctx.fd, temp[:])
ok = true
return
@@ -85,12 +97,13 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam
context_create_with_sleep :: proc(filename: string, sleep := 2 * time.Second) -> (ctx: Context, ok: bool) #optional_ok {
freq, freq_ok := time.tsc_frequency(sleep)
timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1
timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000_000) if freq_ok else 1
return context_create_with_scale(filename, freq_ok, timestamp_scale)
}
context_create :: proc{context_create_with_scale, context_create_with_sleep}
@(no_instrumentation)
context_destroy :: proc(ctx: ^Context) {
if ctx == nil {
return
@@ -102,25 +115,39 @@ context_destroy :: proc(ctx: ^Context) {
buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok {
assert(len(data) >= 1024)
buffer.data = data
buffer.tid = tid
buffer.pid = pid
buffer.head = 0
buffer.data = data
buffer.tid = tid
buffer.pid = pid
buffer.first_ts = 0
buffer.head = size_of(Manual_Buffer_Header)
ok = true
return
}
@(no_instrumentation)
buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(buffer.data) == 0 {
return
}
buffer_size := buffer.head - size_of(Manual_Buffer_Header)
hdr := (^Manual_Buffer_Header)(raw_data(buffer.data))
hdr.size = u32le(buffer_size)
hdr.pid = u32le(buffer.pid)
hdr.tid = u32le(buffer.tid)
hdr.first_ts = u64le(buffer.first_ts)
start := _trace_now(ctx)
write(ctx.fd, buffer.data[:buffer.head])
buffer.head = 0
buffer.head = size_of(Manual_Buffer_Header)
end := _trace_now(ctx)
buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start, buffer.tid, buffer.pid)
buffer.head += _build_end(buffer.data[buffer.head:], end, buffer.tid, buffer.pid)
buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start)
buffer.head += _build_end(buffer.data[buffer.head:], end)
buffer.first_ts = end
}
@(no_instrumentation)
buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) {
buffer_flush(ctx, buffer)
@@ -130,35 +157,37 @@ buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) {
@(deferred_in=_scoped_buffer_end)
@(no_instrumentation)
SCOPED_EVENT :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) -> bool {
_buffer_begin(ctx, buffer, name, args, location)
return true
}
@(private)
@(no_instrumentation)
_scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #caller_location) {
_buffer_end(ctx, buffer)
}
@(no_instrumentation)
_trace_now :: proc "contextless" (ctx: ^Context) -> f64 {
_trace_now :: proc "contextless" (ctx: ^Context) -> u64 {
if !ctx.precise_time {
return f64(tick_now()) / 1_000
return u64(tick_now())
}
return f64(intrinsics.read_cycle_counter())
return u64(intrinsics.read_cycle_counter())
}
@(no_instrumentation)
_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
header_size = size_of(Manual_Header)
_build_stream_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
header_size = size_of(Manual_Stream_Header)
if header_size > len(buffer) {
return 0, false
}
hdr := (^Manual_Header)(raw_data(buffer))
hdr := (^Manual_Stream_Header)(raw_data(buffer))
hdr.magic = MANUAL_MAGIC
hdr.version = 1
hdr.version = 3
hdr.timestamp_scale = f64le(timestamp_scale)
hdr.reserved = 0
ok = true
@@ -166,7 +195,7 @@ _build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (hea
}
@(no_instrumentation)
_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ {
_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: u64) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ {
ev := (^Begin_Event)(raw_data(buffer))
name_len := min(len(name), 255)
args_len := min(len(args), 255)
@@ -177,9 +206,7 @@ _build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, ar
}
ev.type = .Begin
ev.pid = u32le(pid)
ev.tid = u32le(tid)
ev.ts = f64le(ts)
ev.ts = u64le(ts)
ev.name_len = u8(name_len)
ev.args_len = u8(args_len)
intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len)
@@ -190,7 +217,7 @@ _build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, ar
}
@(no_instrumentation)
_build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
_build_end :: proc "contextless" (buffer: []u8, ts: u64) -> (event_size: int, ok: bool) #optional_ok {
ev := (^End_Event)(raw_data(buffer))
event_size = size_of(End_Event)
if event_size > len(buffer) {
@@ -198,9 +225,28 @@ _build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) ->
}
ev.type = .End
ev.pid = u32le(pid)
ev.tid = u32le(tid)
ev.ts = f64le(ts)
ev.ts = u64le(ts)
ok = true
return
}
@(no_instrumentation)
_build_name_event :: #force_inline proc "contextless" (buffer: []u8, name: string, type: Manual_Event_Type) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ {
ev := (^Name_Event)(raw_data(buffer))
name_len := min(len(name), 255)
event_size = size_of(Name_Event) + name_len
if event_size > len(buffer) {
return 0, false
}
if type != .Name_Process && type != .Name_Thread {
return 0, false
}
ev.type = type
ev.name_len = u8(name_len)
intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Name_Event):]), raw_data(name), name_len)
ok = true
return
@@ -212,7 +258,7 @@ _buffer_begin :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: strin
buffer_flush(ctx, buffer)
}
name := location.procedure if name == "" else name
buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid)
buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx))
}
@(no_instrumentation)
@@ -223,7 +269,23 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch
buffer_flush(ctx, buffer)
}
buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid)
buffer.head += _build_end(buffer.data[buffer.head:], ts)
}
@(no_instrumentation)
_buffer_name_thread :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ {
if buffer.head + NAME_EVENT_MAX > len(buffer.data) {
buffer_flush(ctx, buffer)
}
buffer.head += _build_name_event(buffer.data[buffer.head:], name, .Name_Thread)
}
@(no_instrumentation)
_buffer_name_process :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ {
if buffer.head + NAME_EVENT_MAX > len(buffer.data) {
buffer_flush(ctx, buffer)
}
buffer.head += _build_name_event(buffer.data[buffer.head:], name, .Name_Process)
}
@(no_instrumentation)

View File

@@ -511,9 +511,12 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
io.write_i64(w, i64(8*ti.size), 10, &n) or_return
case Type_Info_String:
if info.is_cstring {
io.write_string(w, "cstring", &n) or_return
} else {
io.write_string(w, "string", &n) or_return
io.write_byte(w, 'c', &n) or_return
}
io.write_string(w, "string", &n) or_return
switch info.encoding {
case .UTF_8: /**/
case .UTF_16: io.write_string(w, "16", &n) or_return
}
case Type_Info_Boolean:
switch ti.id {
@@ -630,7 +633,7 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
io.write_string(w, "struct ", &n) or_return
if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return }
if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return }
if .no_copy in info.flags { io.write_string(w, "#no_copy ", &n) or_return }
// if .no_copy in info.flags { io.write_string(w, "#no_copy ", &n) or_return }
if .align in info.flags {
io.write_string(w, "#align(", &n) or_return
io.write_i64(w, i64(ti.align), 10, &n) or_return

View File

@@ -2440,6 +2440,57 @@ Graphically, the operation looks as follows. The `t` and `f` represent the
*/
select :: intrinsics.simd_select
/*
Runtime Equivalent to Shuffle.
Performs element-wise table lookups using runtime indices.
Each element in the indices vector selects an element from the table vector.
The indices are automatically masked to prevent out-of-bounds access.
This operation is hardware-accelerated on most platforms when using 8-bit
integer vectors. For other element types or unsupported vector sizes, it
falls back to software emulation.
Inputs:
- `table`: The lookup table vector (should be power-of-2 size for correct masking).
- `indices`: The indices vector (automatically masked to valid range).
Returns:
- A vector where `result[i] = table[indices[i] & (table_size-1)]`.
Operation:
for i in 0 ..< len(indices) {
masked_index := indices[i] & (len(table) - 1)
result[i] = table[masked_index]
}
return result
Implementation:
| Platform | Lane Size | Implementation |
|-------------|-------------------------------------------|---------------------|
| x86-64 | pshufb (16B), vpshufb (32B), AVX512 (64B) | Single vector |
| ARM64 | tbl1 (16B), tbl2 (32B), tbl4 (64B) | Automatic splitting |
| ARM32 | vtbl1 (8B), vtbl2 (16B), vtbl4 (32B) | Automatic splitting |
| WebAssembly | i8x16.swizzle (16B), Emulation (>16B) | Mixed |
| Other | Emulation | Software |
Example:
import "core:simd"
import "core:fmt"
runtime_swizzle_example :: proc() {
table := simd.u8x16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
indices := simd.u8x16{15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
result := simd.runtime_swizzle(table, indices)
fmt.println(result) // Expected: {15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
}
*/
runtime_swizzle :: intrinsics.simd_runtime_swizzle
/*
Compute the square root of each lane in a SIMD vector.
*/

View File

@@ -47,12 +47,12 @@ wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {
guard(&wg.mutex)
atomic_add(&wg.counter, delta)
if wg.counter < 0 {
switch counter := atomic_load(&wg.counter); {
case counter < 0:
panic_contextless("sync.Wait_Group negative counter")
}
if wg.counter == 0 {
case wg.counter == 0:
cond_broadcast(&wg.cond)
if wg.counter != 0 {
if atomic_load(&wg.counter) != 0 {
panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
@@ -78,11 +78,8 @@ wait group's internal counter reaches zero.
wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {
guard(&wg.mutex)
if wg.counter != 0 {
for atomic_load(&wg.counter) != 0 {
cond_wait(&wg.cond, &wg.mutex)
if wg.counter != 0 {
panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}
@@ -100,13 +97,10 @@ wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: t
}
guard(&wg.mutex)
if wg.counter != 0 {
for atomic_load(&wg.counter) != 0 {
if !cond_wait_with_timeout(&wg.cond, &wg.mutex, duration) {
return false
}
if wg.counter != 0 {
panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
return true
}

View File

@@ -0,0 +1,50 @@
package objc_Foundation
import "base:intrinsics"
@(objc_class="NSBitmapImageRep")
BitmapImageRep :: struct { using _: Object }
@(objc_type=BitmapImageRep, objc_name="alloc", objc_is_class_method=true)
BitmapImageRep_alloc :: proc "c" () -> ^BitmapImageRep {
return msgSend(^BitmapImageRep, BitmapImageRep, "alloc")
}
@(objc_type=BitmapImageRep, objc_name="initWithBitmapDataPlanes")
BitmapImageRep_initWithBitmapDataPlanes :: proc "c" (
self: ^BitmapImageRep,
bitmapDataPlanes: ^^u8,
pixelsWide: Integer,
pixelsHigh: Integer,
bitsPerSample: Integer,
samplesPerPixel: Integer,
hasAlpha: bool,
isPlanar: bool,
colorSpaceName: ^String,
bytesPerRow: Integer,
bitsPerPixel: Integer) -> ^BitmapImageRep {
return msgSend(^BitmapImageRep,
self,
"initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:",
bitmapDataPlanes,
pixelsWide,
pixelsHigh,
bitsPerSample,
samplesPerPixel,
hasAlpha,
isPlanar,
colorSpaceName,
bytesPerRow,
bitsPerPixel)
}
@(objc_type=BitmapImageRep, objc_name="bitmapData")
BitmapImageRep_bitmapData :: proc "c" (self: ^BitmapImageRep) -> rawptr {
return msgSend(rawptr, self, "bitmapData")
}
@(objc_type=BitmapImageRep, objc_name="CGImage")
BitmapImageRep_CGImage :: proc "c" (self: ^BitmapImageRep) -> rawptr {
return msgSend(rawptr, self, "CGImage")
}

View File

@@ -568,6 +568,14 @@ window_delegate_register_and_alloc :: proc(template: WindowDelegateTemplate, cla
@(objc_class="CALayer")
Layer :: struct { using _: Object }
@(objc_type=Layer, objc_name="contents")
Layer_contents :: proc "c" (self: ^Layer) -> rawptr {
return msgSend(rawptr, self, "contents")
}
@(objc_type=Layer, objc_name="setContents")
Layer_setContents :: proc "c" (self: ^Layer, contents: rawptr) {
msgSend(nil, self, "setContents:", contents)
}
@(objc_type=Layer, objc_name="contentsScale")
Layer_contentsScale :: proc "c" (self: ^Layer) -> Float {
return msgSend(Float, self, "contentsScale")

View File

@@ -5,15 +5,34 @@ foreign import mach "system:System"
import "core:c"
import "base:intrinsics"
kern_return_t :: distinct c.int
mach_port_t :: distinct c.uint
task_t :: mach_port_t
semaphore_t :: distinct u64
kern_return_t :: distinct c.int
thread_act_t :: distinct u64
thread_state_t :: distinct ^u32
thread_list_t :: [^]thread_act_t
vm_region_recurse_info_t :: distinct ^i32
task_info_t :: distinct ^i32
MACH_PORT_NULL :: 0
MACH_PORT_DEAD :: ~mach_port_t(0)
MACH_MSG_PORT_DESCRIPTOR :: 0
X86_THREAD_STATE32 :: 1
X86_THREAD_STATE64 :: 4
ARM_THREAD_STATE64 :: 6
mach_msg_option_t :: distinct i32
name_t :: distinct cstring
vm_map_t :: mach_port_t
mem_entry_name_port_t :: mach_port_t
ipc_space_t :: mach_port_t
thread_t :: mach_port_t
task_t :: mach_port_t
semaphore_t :: mach_port_t
vm_size_t :: distinct c.uintptr_t
@@ -29,11 +48,279 @@ vm_inherit_t :: distinct c.uint
mach_port_name_t :: distinct c.uint
mach_port_right_t :: distinct c.uint
sync_policy_t :: distinct c.int
mach_msg_port_descriptor_t :: struct {
name: mach_port_t,
_: u32,
using _: bit_field u32 {
_: u32 | 16,
disposition: u32 | 8,
type: u32 | 8,
},
}
Task_Port_Type :: enum u32 {
Kernel = 1,
Host,
Name,
Bootstrap,
Seatbelt = 7,
Access = 9,
}
Bootstrap_Error :: enum u32 {
Success,
Not_Privileged = 1100,
Name_In_Use = 1101,
Unknown_Service = 1102,
Service_Active = 1103,
Bad_Count = 1104,
No_Memory = 1105,
No_Children = 1106,
}
Msg_Type :: enum u32 {
Unstructured = 0,
Bit = 0,
Boolean = 0,
Integer_16 = 1,
Integer_32 = 2,
Char = 8,
Byte = 9,
Integer_8 = 9,
Real = 10,
Integer_64 = 11,
String = 12,
String_C = 12,
Port_Name = 15,
Move_Receive = 16,
Port_Receive = 16,
Move_Send = 17,
Port_Send = 17,
Move_Send_Once = 18,
Port_Send_Once = 18,
Copy_Send = 19,
Make_Send = 20,
Make_Send_Once = 21,
}
Msg_Header_Bits :: enum u32 {
Zero = 0,
Remote_Mask = 0xff,
Local_Mask = 0xff00,
Migrated = 0x08000000,
Unused = 0x07ff0000,
Complex_Data = 0x10000000,
Complex_Ports = 0x20000000,
Circular = 0x40000000,
Complex = 0x80000000,
}
mach_msg_type_t :: struct {
using _: bit_field u32 {
name: u32 | 8,
size: u32 | 8,
number: u32 | 12,
inline: u32 | 1,
longform: u32 | 1,
deallocate: u32 | 1,
unused: u32 | 1,
},
}
mach_msg_header_t :: struct {
msgh_bits: u32,
msgh_size: u32,
msgh_remote_port: mach_port_t,
msgh_local_port: mach_port_t,
msgh_voucher_port: u32,
msgh_id: i32,
}
mach_msg_body_t :: struct {
msgh_descriptor_count: u32,
}
mach_msg_trailer_t :: struct {
msgh_trailer_type: u32,
msgh_trailer_size: u32,
}
x86_thread_state32_t :: struct {
eax: u32,
ebx: u32,
ecx: u32,
edx: u32,
edi: u32,
esi: u32,
ebp: u32,
esp: u32,
ss: u32,
eflags: u32,
eip: u32,
cs: u32,
ds: u32,
es: u32,
fs: u32,
gs: u32,
}
X86_THREAD_STATE32_COUNT :: size_of(x86_thread_state32_t) / size_of(u32)
x86_thread_state64_t :: struct #packed {
rax: u64,
rbx: u64,
rcx: u64,
rdx: u64,
rdi: u64,
rsi: u64,
rbp: u64,
rsp: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
rip: u64,
rflags: u64,
cs: u64,
fs: u64,
gs: u64,
}
X86_THREAD_STATE64_COUNT :: size_of(x86_thread_state64_t) / size_of(u32)
arm_thread_state64_t :: struct #packed {
x: [29]u64,
fp: u64,
lr: u64,
sp: u64,
pc: u64,
cpsr: u32,
pad: u32,
}
ARM_THREAD_STATE64_COUNT :: size_of(arm_thread_state64_t) / size_of(u32)
THREAD_IDENTIFIER_INFO :: 4
thread_identifier_info :: struct {
thread_id: u64,
thread_handler: u64,
dispatch_qaddr: u64,
}
THREAD_IDENTIFIER_INFO_COUNT :: size_of(thread_identifier_info) / size_of(u32)
vm_region_submap_info_64 :: struct {
protection: u32,
max_protection: u32,
inheritance: u32,
offset: u64,
user_tag: u32,
pages_residept: u32,
pages_shared_now_private: u32,
pages_swapped_out: u32,
pages_dirtied: u32,
ref_count: u32,
shadow_depth: u16,
external_pager: u8,
share_mode: u8,
is_submap: b32,
behavior: i32,
object_id: u32,
user_wired_count: u16,
pages_reusable: u32,
}
VM_REGION_SUBMAP_INFO_COUNT_64 :: size_of(vm_region_submap_info_64) / size_of(u32)
TASK_DYLD_INFO :: 17
task_dyld_info :: struct {
all_image_info_addr: u64,
all_image_info_size: u64,
all_image_info_format: i32,
}
TASK_DYLD_INFO_COUNT :: size_of(task_dyld_info) / size_of(u32)
dyld_image_info :: struct {
image_load_addr: u64,
image_file_path: cstring,
image_file_mod_date: u64,
}
dyld_uuid_info :: struct {
image_load_addr: u64,
image_uuid: [16]u8,
}
dyld_all_image_infos :: struct {
version: u32,
info_array_count: u32,
info_array: rawptr,
notification: rawptr,
process_detached_from_shared_region: b32,
libSystem_initialized: b32,
dyld_image_load_addr: u64,
jit_info: rawptr,
dyld_version: cstring,
error_message: cstring,
termination_flags: u64,
core_symbolication_shm_page: rawptr,
system_order_flag: u64,
uuid_array_count: u64,
uuid_array: rawptr,
dyld_all_image_infos_addr: u64,
initial_image_count: u64,
error_kind: u64,
error_client_of_dylib_path: cstring,
error_target_dylib_path: cstring,
error_symbol: cstring,
shared_cache_slide: u64,
shared_cache_uuid: [16]u8,
shared_cache_base_addr: u64,
info_array_change_timestamp: u64,
dyld_path: cstring,
notify_ports: [8]mach_port_t,
reserved: [7]u64,
shared_cache_fsid: u64,
shared_cache_fsobjid: u64,
compact_dyld_image_info_addr: u64,
compact_dyld_image_info_size: u64,
platform: u32,
aot_info_count: u32,
aot_info_array: rawptr,
aot_info_array_change_timestamp: u64,
aot_shared_cache_base_address: u64,
aot_shared_cache_uuid: [16]u8,
}
@(default_calling_convention="c")
foreign mach {
mach_task_self :: proc() -> mach_port_t ---
mach_task_self :: proc() -> mach_port_t ---
mach_msg :: proc(header: rawptr, option: Msg_Option_Flags, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> Kern_Return ---
mach_msg_send :: proc(header: rawptr) -> Kern_Return ---
mach_vm_allocate :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> Kern_Return ---
mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> Kern_Return ---
mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: VM_Inherit) -> Kern_Return ---
mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> Kern_Return ---
vm_page_size: u64
vm_page_mask: u64
vm_page_shift: i32
mach_port_allocate :: proc(task: task_t, right: Port_Right, name: rawptr) -> Kern_Return ---
mach_port_deallocate :: proc(task: task_t, name: u32) -> Kern_Return ---
mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> Kern_Return ---
task_get_special_port :: proc(task: task_t, port: i32, special_port: ^mach_port_t) -> Kern_Return ---
task_suspend :: proc(task: task_t) -> Kern_Return ---
task_resume :: proc(task: task_t) -> Kern_Return ---
task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> Kern_Return ---
task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> Kern_Return ---
task_terminate :: proc(task: task_t) -> Kern_Return ---
semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return ---
semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return ---
@@ -44,9 +331,11 @@ foreign mach {
semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return ---
vm_allocate :: proc (target_task : vm_map_t, address: ^vm_address_t, size: vm_size_t, flags: VM_Flags) -> Kern_Return ---
thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> Kern_Return ---
thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> Kern_Return ---
vm_deallocate :: proc(target_task: vm_map_t, address: vm_address_t, size: vm_size_t) -> Kern_Return ---
bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> Kern_Return ---
bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> Kern_Return ---
vm_map :: proc(
target_task: vm_map_t,
@@ -70,15 +359,10 @@ foreign mach {
object_handle: ^mem_entry_name_port_t,
parent_entry: mem_entry_name_port_t,
) -> Kern_Return ---
mach_port_deallocate :: proc(
task: ipc_space_t,
name: mach_port_name_t,
) -> Kern_Return ---
vm_page_size: vm_size_t
}
Kern_Return :: enum kern_return_t {
Success,
@@ -500,6 +784,39 @@ VM_PROT_NONE :: VM_Prot_Flags{}
VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write}
VM_PROT_ALL :: VM_Prot_Flags{.Read, .Write, .Execute}
/*
* Mach msg options, defined as bits within the mach_msg_option_t type
*/
Msg_Option :: enum mach_msg_option_t {
Send_Msg,
Receive_Msg,
Send_Timeout = LOG2(0x10),
Send_Notify = LOG2(0x20),
Send_Interrupt = LOG2(0x40),
Send_Cancel = LOG2(0x80),
Receive_Timeout = LOG2(0x100),
Receive_Notify = LOG2(0x200),
Receive_Interrupt = LOG2(0x400),
Receive_Large = LOG2(0x800),
Send_Always = LOG2(0x10000),
}
Msg_Option_Flags :: distinct bit_set[Msg_Option; mach_msg_option_t]
/*
* Enumeration of valid values for mach_port_right_t
*/
Port_Right :: enum mach_port_right_t {
Send,
Receive,
Send_Once,
Port_Set,
Dead_Name,
}
/*
* Enumeration of valid values for vm_inherit_t.
*/
@@ -522,3 +839,7 @@ Sync_Policy :: enum sync_policy_t {
Lifo = Fifo | Reversed,
}
mach_vm_trunc_page :: proc(v: u64) -> u64 {
return v & ~vm_page_mask
}

View File

@@ -3,23 +3,13 @@ package darwin
// #define OS_WAIT_ON_ADDR_AVAILABILITY \
// __API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4))
when ODIN_OS == .Darwin {
when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 17_04_00 {
WAIT_ON_ADDRESS_AVAILABLE :: true
} else when ODIN_MINIMUM_OS_VERSION >= 14_04_00 {
WAIT_ON_ADDRESS_AVAILABLE :: true
when ODIN_PLATFORM_SUBTARGET_IOS {
WAIT_ON_ADDRESS_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 17_04_00
ULOCK_WAIT_2_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 14_00_00
} else {
WAIT_ON_ADDRESS_AVAILABLE :: false
WAIT_ON_ADDRESS_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 14_04_00
ULOCK_WAIT_2_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 11_00_00
}
when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 14_00_00 {
ULOCK_WAIT_2_AVAILABLE :: true
} else when ODIN_MINIMUM_OS_VERSION >= 11_00_00 {
ULOCK_WAIT_2_AVAILABLE :: true
} else {
ULOCK_WAIT_2_AVAILABLE :: false
}
} else {
WAIT_ON_ADDRESS_AVAILABLE :: false
ULOCK_WAIT_2_AVAILABLE :: false

View File

@@ -52,7 +52,7 @@ CPU :: struct {
cpu: CPU
@(init, private)
init_cpu_features :: proc "c" () {
init_cpu_features :: proc "contextless" () {
is_set :: #force_inline proc "c" (bit: u32, value: u32) -> bool {
return (value>>bit) & 0x1 != 0
}
@@ -156,7 +156,7 @@ init_cpu_features :: proc "c" () {
_cpu_name_buf: [72]u8
@(init, private)
init_cpu_name :: proc "c" () {
init_cpu_name :: proc "contextless" () {
number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
if number_of_extended_ids < 0x8000_0004 {
return

View File

@@ -2,11 +2,13 @@
#+build linux
package sysinfo
import "base:runtime"
import "core:sys/linux"
import "core:strings"
@(init, private)
init_cpu_features :: proc() {
init_cpu_features :: proc "contextless" () {
context = runtime.default_context()
fd, err := linux.open("/proc/cpuinfo", {})
if err != .NONE { return }
defer linux.close(fd)

View File

@@ -2,12 +2,15 @@
#+build linux
package sysinfo
import "base:runtime"
import "core:sys/linux"
import "core:strings"
import "core:strconv"
@(init, private)
init_cpu_core_count :: proc() {
init_cpu_core_count :: proc "contextless" () {
context = runtime.default_context()
fd, err := linux.open("/proc/cpuinfo", {})
if err != .NONE { return }
defer linux.close(fd)

View File

@@ -7,7 +7,7 @@ import "base:intrinsics"
import "core:sys/linux"
@(init, private)
init_cpu_features :: proc() {
init_cpu_features :: proc "contextless" () {
_features: CPU_Features
defer cpu.features = _features
@@ -81,11 +81,11 @@ init_cpu_features :: proc() {
}
err := linux.riscv_hwprobe(raw_data(pairs), len(pairs), 0, nil, {})
if err != nil {
assert(err == .ENOSYS, "unexpected error from riscv_hwprobe()")
assert_contextless(err == .ENOSYS, "unexpected error from riscv_hwprobe()")
return
}
assert(pairs[0].key == .IMA_EXT_0)
assert_contextless(pairs[0].key == .IMA_EXT_0)
exts := pairs[0].value.ima_ext_0
exts -= { .FD, .C, .V }
_features += transmute(CPU_Features)exts
@@ -97,7 +97,7 @@ init_cpu_features :: proc() {
_features += { .Misaligned_Supported }
}
} else {
assert(pairs[1].key == .CPUPERF_0)
assert_contextless(pairs[1].key == .CPUPERF_0)
if .FAST in pairs[1].value.cpu_perf_0 {
_features += { .Misaligned_Supported, .Misaligned_Fast }
} else if .UNSUPPORTED not_in pairs[1].value.cpu_perf_0 {
@@ -108,6 +108,6 @@ init_cpu_features :: proc() {
}
@(init, private)
init_cpu_name :: proc() {
init_cpu_name :: proc "contextless" () {
cpu.name = "RISCV64"
}

View File

@@ -2,9 +2,12 @@ package sysinfo
import sys "core:sys/windows"
import "base:intrinsics"
import "base:runtime"
@(init, private)
init_cpu_core_count :: proc() {
init_cpu_core_count :: proc "contextless" () {
context = runtime.default_context()
infos: []sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION
defer delete(infos)

View File

@@ -10,7 +10,9 @@ import "base:runtime"
version_string_buf: [1024]u8
@(init, private)
init_os_version :: proc () {
init_os_version :: proc "contextless" () {
context = runtime.default_context()
when ODIN_OS == .NetBSD {
os_version.platform = .NetBSD
} else {
@@ -66,7 +68,7 @@ init_os_version :: proc () {
}
@(init, private)
init_ram :: proc() {
init_ram :: proc "contextless" () {
// Retrieve RAM info using `sysctl`
mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM64}
mem_size: u64

View File

@@ -1,5 +1,7 @@
package sysinfo
import "base:runtime"
import "core:strconv"
import "core:strings"
import "core:sys/unix"
@@ -9,7 +11,8 @@ import NS "core:sys/darwin/Foundation"
version_string_buf: [1024]u8
@(init, private)
init_platform :: proc() {
init_platform :: proc "contextless" () {
context = runtime.default_context()
ws :: strings.write_string
wi :: strings.write_int
@@ -28,7 +31,7 @@ init_platform :: proc() {
macos_version = {int(version.majorVersion), int(version.minorVersion), int(version.patchVersion)}
when ODIN_PLATFORM_SUBTARGET == .iOS {
when ODIN_PLATFORM_SUBTARGET_IOS {
os_version.platform = .iOS
ws(&b, "iOS")
} else {

View File

@@ -9,7 +9,9 @@ import "base:runtime"
version_string_buf: [1024]u8
@(init, private)
init_os_version :: proc () {
init_os_version :: proc "contextless" () {
context = runtime.default_context()
os_version.platform = .FreeBSD
kernel_version_buf: [1024]u8
@@ -68,7 +70,7 @@ init_os_version :: proc () {
}
@(init, private)
init_ram :: proc() {
init_ram :: proc "contextless" () {
// Retrieve RAM info using `sysctl`
mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM}
mem_size: u64

View File

@@ -1,6 +1,7 @@
package sysinfo
import "base:intrinsics"
import "base:runtime"
import "core:strconv"
import "core:strings"
@@ -10,7 +11,9 @@ import "core:sys/linux"
version_string_buf: [1024]u8
@(init, private)
init_os_version :: proc () {
init_os_version :: proc "contextless" () {
context = runtime.default_context()
os_version.platform = .Linux
b := strings.builder_from_bytes(version_string_buf[:])
@@ -91,11 +94,11 @@ init_os_version :: proc () {
}
@(init, private)
init_ram :: proc() {
init_ram :: proc "contextless" () {
// Retrieve RAM info using `sysinfo`
sys_info: linux.Sys_Info
errno := linux.sysinfo(&sys_info)
assert(errno == .NONE, "Good luck to whoever's debugging this, something's seriously cucked up!")
assert_contextless(errno == .NONE, "Good luck to whoever's debugging this, something's seriously cucked up!")
ram = RAM{
total_ram = int(sys_info.totalram) * int(sys_info.mem_unit),
free_ram = int(sys_info.freeram) * int(sys_info.mem_unit),

View File

@@ -12,7 +12,9 @@ import "base:runtime"
version_string_buf: [1024]u8
@(init, private)
init_os_version :: proc () {
init_os_version :: proc "contextless" () {
context = runtime.default_context()
/*
NOTE(Jeroen):
`GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
@@ -43,6 +45,7 @@ init_os_version :: proc () {
os_version.minor = int(osvi.dwMinorVersion)
os_version.build[0] = int(osvi.dwBuildNumber)
b := strings.builder_from_bytes(version_string_buf[:])
strings.write_string(&b, "Windows ")
@@ -259,7 +262,7 @@ init_os_version :: proc () {
}
@(init, private)
init_ram :: proc() {
init_ram :: proc "contextless" () {
state: sys.MEMORYSTATUSEX
state.dwLength = size_of(state)
@@ -276,10 +279,11 @@ init_ram :: proc() {
}
@(init, private)
init_gpu_info :: proc() {
init_gpu_info :: proc "contextless" () {
GPU_INFO_BASE :: "SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\"
context = runtime.default_context()
gpu_list: [dynamic]GPU
gpu_index: int
@@ -324,8 +328,8 @@ read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok
status := sys.RegGetValueW(
hkey,
&key_name_wide[0],
&val_name_wide[0],
cstring16(&key_name_wide[0]),
cstring16(&val_name_wide[0]),
sys.RRF_RT_REG_SZ,
nil,
raw_data(result_wide[:]),
@@ -359,8 +363,8 @@ read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool
result_size := sys.DWORD(size_of(i32))
status := sys.RegGetValueW(
hkey,
&key_name_wide[0],
&val_name_wide[0],
cstring16(&key_name_wide[0]),
cstring16(&val_name_wide[0]),
sys.RRF_RT_REG_DWORD,
nil,
&res,
@@ -386,8 +390,8 @@ read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool
result_size := sys.DWORD(size_of(i64))
status := sys.RegGetValueW(
hkey,
&key_name_wide[0],
&val_name_wide[0],
cstring16(&key_name_wide[0]),
cstring16(&val_name_wide[0]),
sys.RRF_RT_REG_QWORD,
nil,
&res,

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