mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-25 21:28:13 +00:00
update to master
This commit is contained in:
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -36,6 +36,8 @@ jobs:
|
||||
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
(cd tests/issues; ./run.sh)
|
||||
./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
|
||||
build_freebsd:
|
||||
name: FreeBSD Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
@@ -64,6 +66,7 @@ jobs:
|
||||
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
(cd tests/issues; ./run.sh)
|
||||
./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
ci:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -128,6 +131,8 @@ jobs:
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
|
||||
- name: Check benchmarks
|
||||
run: ./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
@@ -203,6 +208,11 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
- name: Check benchmarks
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
- name: Odin documentation tests
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
1
.github/workflows/nightly.yml
vendored
1
.github/workflows/nightly.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
include-hidden-files: true
|
||||
name: windows_artifacts
|
||||
path: dist
|
||||
build_linux:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -266,6 +266,9 @@ bin/
|
||||
*.exe
|
||||
*.obj
|
||||
*.pdb
|
||||
*.res
|
||||
desktop.ini
|
||||
Thumbs.db
|
||||
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
|
||||
@@ -171,14 +171,6 @@ Type_Info_Simd_Vector :: struct {
|
||||
elem_size: int,
|
||||
count: int,
|
||||
}
|
||||
Type_Info_Relative_Pointer :: struct {
|
||||
pointer: ^Type_Info, // ^T
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Relative_Multi_Pointer :: struct {
|
||||
pointer: ^Type_Info, // [^]T
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Matrix :: struct {
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
@@ -241,8 +233,6 @@ Type_Info :: struct {
|
||||
Type_Info_Map,
|
||||
Type_Info_Bit_Set,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Bit_Field,
|
||||
@@ -275,8 +265,6 @@ Typeid_Kind :: enum u8 {
|
||||
Map,
|
||||
Bit_Set,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
Bit_Field,
|
||||
|
||||
@@ -6,6 +6,39 @@ import "base:intrinsics"
|
||||
Maybe :: union($T: typeid) {T}
|
||||
|
||||
|
||||
/*
|
||||
Recovers the containing/parent struct from a pointer to one of its fields.
|
||||
Works by "walking back" to the struct's starting address using the offset between the field and the struct.
|
||||
|
||||
Inputs:
|
||||
- ptr: Pointer to the field of a container struct
|
||||
- T: The type of the container struct
|
||||
- field_name: The name of the field in the `T` struct
|
||||
|
||||
Returns:
|
||||
- A pointer to the container struct based on a pointer to a field in it
|
||||
|
||||
Example:
|
||||
package container_of
|
||||
import "base:runtime"
|
||||
|
||||
Node :: struct {
|
||||
value: int,
|
||||
prev: ^Node,
|
||||
next: ^Node,
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
node: Node
|
||||
field_ptr := &node.next
|
||||
container_struct_ptr: ^Node = runtime.container_of(field_ptr, Node, "next")
|
||||
assert(container_struct_ptr == &node)
|
||||
assert(uintptr(field_ptr) - uintptr(container_struct_ptr) == size_of(node.value) + size_of(node.prev))
|
||||
}
|
||||
|
||||
Output:
|
||||
^Node
|
||||
*/
|
||||
@(builtin, require_results)
|
||||
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
@@ -350,12 +383,23 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
|
||||
return
|
||||
}
|
||||
|
||||
// `make_map` allocates and initializes a map. Like `new`, the first argument is a type, not a value.
|
||||
// `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) {
|
||||
m.allocator = allocator
|
||||
return m
|
||||
}
|
||||
|
||||
// `make_map_cap` initializes a map with an allocator and allocates space using `capacity`.
|
||||
// Like `new`, the first argument is a type, not a value.
|
||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_map_expr_error_loc(loc, capacity)
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -392,6 +436,7 @@ make :: proc{
|
||||
make_dynamic_array_len,
|
||||
make_dynamic_array_len_cap,
|
||||
make_map,
|
||||
make_map_cap,
|
||||
make_multi_pointer,
|
||||
|
||||
make_soa_slice,
|
||||
@@ -894,19 +939,7 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
||||
|
||||
@builtin
|
||||
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
when size_of(S) == 1 {
|
||||
return int(intrinsics.count_ones(transmute(u8)s))
|
||||
} else when size_of(S) == 2 {
|
||||
return int(intrinsics.count_ones(transmute(u16)s))
|
||||
} else when size_of(S) == 4 {
|
||||
return int(intrinsics.count_ones(transmute(u32)s))
|
||||
} else when size_of(S) == 8 {
|
||||
return int(intrinsics.count_ones(transmute(u64)s))
|
||||
} else when size_of(S) == 16 {
|
||||
return int(intrinsics.count_ones(transmute(u128)s))
|
||||
} else {
|
||||
#panic("Unhandled card bit_set size")
|
||||
}
|
||||
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -486,18 +486,6 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_u64(u64(info.count))
|
||||
print_byte(']')
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
print_string("#relative(")
|
||||
print_type(info.base_integer)
|
||||
print_string(") ")
|
||||
print_type(info.pointer)
|
||||
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
print_string("#relative(")
|
||||
print_type(info.base_integer)
|
||||
print_string(") ")
|
||||
print_type(info.pointer)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
print_string("matrix[")
|
||||
|
||||
19
bin/RAD-LICENSE
Normal file
19
bin/RAD-LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2024 Epic Games Tools
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
bin/radlink.exe
Normal file
BIN
bin/radlink.exe
Normal file
Binary file not shown.
44
build.bat
44
build.bat
@@ -19,12 +19,12 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
)
|
||||
)
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do (
|
||||
set CURR_DATE_TIME=%%i
|
||||
)
|
||||
|
||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||
set curr_day=%CURR_DATE_TIME:~6,2%
|
||||
|
||||
:: Make sure this is a decent name and not generic
|
||||
set exe_name=odin.exe
|
||||
@@ -45,7 +45,19 @@ if "%2" == "1" (
|
||||
set nightly=0
|
||||
)
|
||||
|
||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||
if %release_mode% equ 0 (
|
||||
set V1=%curr_year%
|
||||
set V2=%curr_month%
|
||||
set V3=%curr_day%
|
||||
) else (
|
||||
set V1=%curr_year%
|
||||
set V2=%curr_month%
|
||||
set V3=0
|
||||
)
|
||||
set V4=0
|
||||
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
|
||||
set odin_version_raw="dev-%V1%-%V2%"
|
||||
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
rem Parse source code as utf-8 even on shift-jis and other codepages
|
||||
@@ -53,18 +65,30 @@ rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-a
|
||||
set compiler_flags= %compiler_flags% /utf-8
|
||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||
|
||||
rem fileversion is defined as {Major,Minor,Build,Private: u16} so a bit limited
|
||||
set rc_flags=-nologo ^
|
||||
-DV1=%V1% -DV2=%V2% -DV3=%V3% -DV4=%V4% ^
|
||||
-DVF=%odin_version_full% -DNIGHTLY=%nightly%
|
||||
|
||||
where /Q git.exe || goto skip_git_hash
|
||||
if not exist .git\ goto skip_git_hash
|
||||
for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
|
||||
set odin_version_raw=dev-%%i
|
||||
set GIT_SHA=%%j
|
||||
)
|
||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||
set rc_flags=%rc_flags% -DGIT_SHA=%GIT_SHA% -DVP=%odin_version_raw%:%GIT_SHA%
|
||||
) else (
|
||||
set rc_flags=%rc_flags% -DVP=%odin_version_raw%
|
||||
)
|
||||
:skip_git_hash
|
||||
|
||||
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
set rc_flags=%rc_flags% -D_DEBUG
|
||||
) else ( rem Release
|
||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
||||
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
||||
@@ -82,6 +106,8 @@ set libs= ^
|
||||
kernel32.lib ^
|
||||
Synchronization.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
set odin_res=misc\odin.res
|
||||
set odin_rc=misc\odin.rc
|
||||
|
||||
rem DO NOT TOUCH!
|
||||
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
|
||||
@@ -93,7 +119,7 @@ if %tilde_backend% EQU 1 (
|
||||
rem DO NOT TOUCH!
|
||||
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console -MANIFEST:EMBED
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||
@@ -102,19 +128,21 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
)
|
||||
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
set linker_settings=%libs% %odin_res% %linker_flags%
|
||||
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
rc %rc_flags% %odin_rc%
|
||||
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||
mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
|
||||
call build_vendor.bat
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
|
||||
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
||||
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -- Hellope World
|
||||
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -resource:examples/demo/demo.rc -- Hellope World
|
||||
|
||||
rem Many non-compiler devs seem to run debug build but don't realize.
|
||||
if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat release" if you want a faster, release mode compiler.
|
||||
|
||||
@@ -130,7 +130,7 @@ build_odin() {
|
||||
EXTRAFLAGS="-O3"
|
||||
;;
|
||||
release-native)
|
||||
if [ "$OS_ARCH" = "arm64" ]; then
|
||||
if [ "$OS_ARCH" = "arm64" ] || [ "$OS_ARCH" = "aarch64" ]; then
|
||||
# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
|
||||
EXTRAFLAGS="-O3 -mcpu=native"
|
||||
else
|
||||
@@ -152,9 +152,7 @@ build_odin() {
|
||||
}
|
||||
|
||||
run_demo() {
|
||||
if [ $# -eq 0 ] || [ "$1" = "debug" ]; then
|
||||
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||
fi
|
||||
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
@@ -166,14 +164,20 @@ if [ $# -eq 0 ]; then
|
||||
elif [ $# -eq 1 ]; then
|
||||
case $1 in
|
||||
report)
|
||||
[ ! -f "./odin" ] && build_odin debug
|
||||
if [ ! -f "./odin" ]; then
|
||||
build_odin debug
|
||||
run_demo
|
||||
fi
|
||||
./odin report
|
||||
;;
|
||||
debug)
|
||||
build_odin debug
|
||||
run_demo
|
||||
;;
|
||||
*)
|
||||
build_odin $1
|
||||
;;
|
||||
esac
|
||||
run_demo
|
||||
else
|
||||
error "Too many arguments!"
|
||||
fi
|
||||
|
||||
@@ -291,7 +291,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
if x > max || 0xd800 <= x && x <= 0xdfff {
|
||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ The following is a mostly-complete projection of the C11 standard library as def
|
||||
| `<inttypes.h>` | Fully projected |
|
||||
| `<iso646.h>` | Not applicable, use Odin's operators |
|
||||
| `<limits.h>` | Not projected |
|
||||
| `<locale.h>` | Not projected |
|
||||
| `<locale.h>` | Fully projected |
|
||||
| `<math.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<setjmp.h>` | Fully projected |
|
||||
| `<signal.h>` | Fully projected |
|
||||
@@ -70,4 +70,4 @@ with the following copyright.
|
||||
|
||||
```
|
||||
Copyright 2021 Dale Weiler <weilercdale@gmail.com>.
|
||||
```
|
||||
```
|
||||
|
||||
133
core/c/libc/locale.odin
Normal file
133
core/c/libc/locale.odin
Normal file
@@ -0,0 +1,133 @@
|
||||
package libc
|
||||
|
||||
import "core:c"
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
// locale.h - category macros
|
||||
|
||||
foreign libc {
|
||||
/*
|
||||
Sets the components of an object with the type lconv with the values appropriate for the
|
||||
formatting of numeric quantities (monetary and otherwise) according to the rules of the current
|
||||
locale.
|
||||
|
||||
Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale()
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]]
|
||||
*/
|
||||
localeconv :: proc() -> ^lconv ---
|
||||
|
||||
/*
|
||||
Selects the appropriate piece of the global locale, as specified by the category and locale arguments,
|
||||
and can be used to change or query the entire global locale or portions thereof.
|
||||
|
||||
Returns: the current locale if `locale` is `nil`, the set locale otherwise
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]]
|
||||
*/
|
||||
@(link_name=LSETLOCALE)
|
||||
setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring ---
|
||||
}
|
||||
|
||||
Locale_Category :: enum c.int {
|
||||
ALL = LC_ALL,
|
||||
COLLATE = LC_COLLATE,
|
||||
CTYPE = LC_CTYPE,
|
||||
MESSAGES = LC_MESSAGES,
|
||||
MONETARY = LC_MONETARY,
|
||||
NUMERIC = LC_NUMERIC,
|
||||
TIME = LC_TIME,
|
||||
}
|
||||
|
||||
when ODIN_OS == .NetBSD {
|
||||
@(private) LSETLOCALE :: "__setlocale50"
|
||||
} else {
|
||||
@(private) LSETLOCALE :: "setlocale"
|
||||
}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
lconv :: struct {
|
||||
decimal_point: cstring,
|
||||
thousand_sep: cstring,
|
||||
grouping: cstring,
|
||||
int_curr_symbol: cstring,
|
||||
currency_symbol: cstring,
|
||||
mon_decimal_points: cstring,
|
||||
mon_thousands_sep: cstring,
|
||||
mon_grouping: cstring,
|
||||
positive_sign: cstring,
|
||||
negative_sign: cstring,
|
||||
int_frac_digits: c.char,
|
||||
frac_digits: c.char,
|
||||
p_cs_precedes: c.char,
|
||||
p_sep_by_space: c.char,
|
||||
n_cs_precedes: c.char,
|
||||
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"`,
|
||||
}
|
||||
} else {
|
||||
lconv :: struct {
|
||||
decimal_point: cstring,
|
||||
thousand_sep: cstring,
|
||||
grouping: cstring,
|
||||
int_curr_symbol: cstring,
|
||||
currency_symbol: cstring,
|
||||
mon_decimal_points: cstring,
|
||||
mon_thousands_sep: cstring,
|
||||
mon_grouping: cstring,
|
||||
positive_sign: cstring,
|
||||
negative_sign: cstring,
|
||||
int_frac_digits: c.char,
|
||||
frac_digits: c.char,
|
||||
p_cs_precedes: c.char,
|
||||
p_sep_by_space: c.char,
|
||||
n_cs_precedes: c.char,
|
||||
n_sep_by_space: c.char,
|
||||
p_sign_posn: c.char,
|
||||
n_sign_posn: c.char,
|
||||
_int_p_cs_precedes: c.char,
|
||||
_int_n_cs_precedes: c.char,
|
||||
_int_p_sep_by_space: c.char,
|
||||
_int_n_sep_by_space: c.char,
|
||||
_int_p_sign_posn: c.char,
|
||||
_int_n_sign_posn: c.char,
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
|
||||
|
||||
LC_ALL :: 0
|
||||
LC_COLLATE :: 1
|
||||
LC_CTYPE :: 2
|
||||
LC_MESSAGES :: 6
|
||||
LC_MONETARY :: 3
|
||||
LC_NUMERIC :: 4
|
||||
LC_TIME :: 5
|
||||
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
LC_CTYPE :: 0
|
||||
LC_NUMERIC :: 1
|
||||
LC_TIME :: 2
|
||||
LC_COLLATE :: 3
|
||||
LC_MONETARY :: 4
|
||||
LC_MESSAGES :: 5
|
||||
LC_ALL :: 6
|
||||
|
||||
}
|
||||
@@ -235,7 +235,7 @@ atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desire
|
||||
return ok
|
||||
}
|
||||
|
||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
|
||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
|
||||
assert(failure != .release)
|
||||
assert(failure != .acq_rel)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ Example:
|
||||
package main
|
||||
|
||||
import "core:bytes"
|
||||
import "core:compress/zlib"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
@@ -36,7 +37,7 @@ Example:
|
||||
buf: bytes.Buffer
|
||||
|
||||
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
||||
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
||||
err := zlib.inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
||||
defer bytes.buffer_destroy(&buf)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -259,6 +259,7 @@ Inputs:
|
||||
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
||||
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
|
||||
@@ -276,22 +277,50 @@ Returns:
|
||||
- ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
|
||||
*/
|
||||
create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
||||
context.allocator = allocator
|
||||
size_in_bits := max_index - min_index
|
||||
|
||||
if size_in_bits < 0 { return {}, false }
|
||||
|
||||
res = new(Bit_Array, allocator)
|
||||
ok = init(res, max_index, min_index, allocator)
|
||||
res.free_pointer = true
|
||||
|
||||
if !ok { free(res, allocator) }
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to initialize a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
|
||||
The range of bits created by this procedure is `min_index..<max_index`, and the
|
||||
array will be able to expand beyond `max_index` if needed.
|
||||
|
||||
*Allocates (`make(ba.bits)`)*
|
||||
|
||||
Inputs:
|
||||
- max_index: maximum starting index
|
||||
- min_index: minimum starting index (used as a bias)
|
||||
- allocator: (default is context.allocator)
|
||||
*/
|
||||
init :: proc(res: ^Bit_Array, max_index: int, min_index: int = 0, allocator := context.allocator) -> (ok: bool) {
|
||||
size_in_bits := max_index - min_index
|
||||
|
||||
if size_in_bits < 0 { return false }
|
||||
|
||||
legs := size_in_bits >> INDEX_SHIFT
|
||||
if size_in_bits & INDEX_MASK > 0 {legs+=1}
|
||||
bits, err := make([dynamic]u64, legs)
|
||||
ok = err == mem.Allocator_Error.None
|
||||
res = new(Bit_Array)
|
||||
if size_in_bits & INDEX_MASK > 0 { legs += 1 }
|
||||
|
||||
bits, err := make([dynamic]u64, legs, allocator)
|
||||
ok = err == nil
|
||||
|
||||
res.bits = bits
|
||||
res.bias = min_index
|
||||
res.length = max_index - min_index
|
||||
res.free_pointer = true
|
||||
res.free_pointer = false
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Sets all values in the Bit_Array to zero.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ _ROT_16: simd.u32x4 : {16, 16, 16, 16}
|
||||
|
||||
when ODIN_ENDIAN == .Big {
|
||||
@(private = "file")
|
||||
_increment_counter :: #force_inline proc "contextless" (ctx: ^Context) -> simd.u32x4 {
|
||||
_increment_counter :: #force_inline proc "contextless" (ctx: ^_chacha20.Context) -> simd.u32x4 {
|
||||
// In the Big Endian case, the low and high portions in the vector
|
||||
// are flipped, so the 64-bit addition can't be done with a simple
|
||||
// vector add.
|
||||
|
||||
@@ -81,16 +81,18 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
|
||||
// 2. while len(z) mod 8 ≠ 0:
|
||||
// z = z || 0
|
||||
|
||||
// 3. while (len(z)/8) mod w ≠ 0:
|
||||
// 3. while (len(z)/8) mod w != 0:
|
||||
// z = z || 00000000
|
||||
z_len := u128(z_hi) << 64 | u128(z_lo)
|
||||
z_rem := int(z_len % u128(w))
|
||||
pad := _PAD[:w - z_rem]
|
||||
if z_rem != 0 {
|
||||
pad := _PAD[:w - z_rem]
|
||||
|
||||
// We just add the padding to the state, instead of returning z.
|
||||
//
|
||||
// 4. return z.
|
||||
update(ctx, pad)
|
||||
// We just add the padding to the state, instead of returning z.
|
||||
//
|
||||
// 4. return z.
|
||||
update(ctx, pad)
|
||||
}
|
||||
}
|
||||
|
||||
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
|
||||
|
||||
@@ -37,8 +37,8 @@ Example:
|
||||
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
|
||||
}
|
||||
*/
|
||||
load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
|
||||
return _load_library(path, global_symbols)
|
||||
load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (library: Library, did_load: bool) {
|
||||
return _load_library(path, global_symbols, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,8 +98,8 @@ Example:
|
||||
}
|
||||
}
|
||||
*/
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||
return _symbol_address(library, symbol)
|
||||
symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||
return _symbol_address(library, symbol, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -174,4 +174,4 @@ initialize_symbols :: proc(
|
||||
// Returns an error message for the last failed procedure call.
|
||||
last_error :: proc() -> string {
|
||||
return _last_error()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
import "base:runtime"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -10,10 +12,10 @@ _unload_library :: proc(library: Library) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_last_error :: proc() -> string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,38 @@
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
import "base:runtime"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||
flags := posix.RTLD_Flags{.NOW}
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL
|
||||
flags += {.GLOBAL}
|
||||
}
|
||||
lib := os.dlopen(path, flags)
|
||||
|
||||
cpath := strings.clone_to_cstring(path, allocator)
|
||||
defer delete(cpath, allocator)
|
||||
|
||||
lib := posix.dlopen(cpath, flags)
|
||||
return Library(lib), lib != nil
|
||||
}
|
||||
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
return os.dlclose(rawptr(library))
|
||||
return posix.dlclose(posix.Symbol_Table(library)) == 0
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol)
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
csymbol := strings.clone_to_cstring(symbol, allocator)
|
||||
defer delete(csymbol, allocator)
|
||||
|
||||
ptr = posix.dlsym(posix.Symbol_Table(library), csymbol)
|
||||
found = ptr != nil
|
||||
return
|
||||
}
|
||||
|
||||
_last_error :: proc() -> string {
|
||||
err := os.dlerror()
|
||||
err := string(posix.dlerror())
|
||||
return "unknown" if err == "" else err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
|
||||
_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)
|
||||
@@ -19,7 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, allocator)
|
||||
defer delete(c_str, allocator)
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||
@@ -31,4 +33,4 @@ _last_error :: proc() -> string {
|
||||
err := win32.System_Error(win32.GetLastError())
|
||||
err_msg := reflect.enum_string(err)
|
||||
return "unknown" if err_msg == "" else err_msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ to_json :: proc(val: Value, allocator := context.allocator) -> (json.Value, mem.
|
||||
case: return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
if keys_all_strings(v) {
|
||||
|
||||
@@ -442,9 +442,6 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
loc := #caller_location,
|
||||
) -> (out_of_space: bool, err: Unmarshal_Error) {
|
||||
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
|
||||
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
|
||||
elem := any{elem_ptr, elemt.id}
|
||||
|
||||
hdr := _decode_header(d.reader) or_return
|
||||
|
||||
// Double size if out of capacity.
|
||||
@@ -459,6 +456,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
if !ok { return false, .Out_Of_Memory }
|
||||
}
|
||||
|
||||
// Set ptr after potential resizes to avoid invalidation.
|
||||
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
|
||||
elem := any{elem_ptr, elemt.id}
|
||||
|
||||
err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
|
||||
if length == -1 && err == .Break { break }
|
||||
if err != nil { return }
|
||||
@@ -509,7 +510,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||
raw.data = raw_data(data)
|
||||
raw.len = 0
|
||||
raw.cap = length
|
||||
raw.cap = scap
|
||||
raw.allocator = context.allocator
|
||||
|
||||
_ = assign_array(d, raw, t.elem, length) or_return
|
||||
@@ -627,7 +628,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
unknown := length == -1
|
||||
fields := reflect.struct_fields_zipped(ti.id)
|
||||
|
||||
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||
idx := 0
|
||||
for ; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||
// Decode key, keys can only be strings.
|
||||
key: string
|
||||
if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break {
|
||||
@@ -662,6 +664,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
|
||||
// Skips unused map entries.
|
||||
if use_field_idx < 0 {
|
||||
val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
|
||||
destroy(val, context.temp_allocator)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -672,6 +676,17 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
fany := any{ptr, field.type.id}
|
||||
_unmarshal_value(d, fany, _decode_header(r) or_return) or_return
|
||||
}
|
||||
|
||||
// If there are fields left in the map that did not get decoded into the struct, decode and discard them.
|
||||
if !unknown {
|
||||
for _ in idx..<length {
|
||||
key := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
|
||||
destroy(key, context.temp_allocator)
|
||||
val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
|
||||
destroy(val, context.temp_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Map:
|
||||
|
||||
@@ -117,7 +117,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
layer.name = read_name(r) or_return
|
||||
layer.components = read_value(r, u8) or_return
|
||||
type := read_value(r, Layer_Data_Type) or_return
|
||||
if type > max(type) {
|
||||
if type > max(Layer_Data_Type) {
|
||||
if r.print_error {
|
||||
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
|
||||
r.filename, u8(type), u8(max(Layer_Data_Type)))
|
||||
|
||||
@@ -154,6 +154,7 @@ write_section :: proc(w: io.Writer, name: string, n_written: ^int = nil) -> (n:
|
||||
io.write_byte (w, '[', &n) or_return
|
||||
io.write_string(w, name, &n) or_return
|
||||
io.write_byte (w, ']', &n) or_return
|
||||
io.write_byte (w, '\n', &n) or_return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -192,12 +192,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
case runtime.Type_Info_Simd_Vector:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Relative_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Relative_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
return .Unsupported_Type
|
||||
|
||||
@@ -172,20 +172,33 @@ assign_float :: proc(val: any, f: $T) -> bool {
|
||||
|
||||
|
||||
@(private)
|
||||
unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
|
||||
unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> (ok: bool, err: Error) {
|
||||
val := val
|
||||
switch &dst in val {
|
||||
case string:
|
||||
dst = str
|
||||
return true
|
||||
return true, nil
|
||||
case cstring:
|
||||
if str == "" {
|
||||
dst = strings.clone_to_cstring("", p.allocator)
|
||||
a_err: runtime.Allocator_Error
|
||||
dst, a_err = strings.clone_to_cstring("", p.allocator)
|
||||
#partial switch a_err {
|
||||
case nil:
|
||||
// okay
|
||||
case .Out_Of_Memory:
|
||||
err = .Out_Of_Memory
|
||||
case:
|
||||
err = .Invalid_Allocator
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// NOTE: This is valid because 'clone_string' appends a NUL terminator
|
||||
dst = cstring(raw_data(str))
|
||||
}
|
||||
return true
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
#partial switch variant in ti.variant {
|
||||
@@ -193,31 +206,37 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.T
|
||||
for name, i in variant.names {
|
||||
if name == str {
|
||||
assign_int(val, variant.values[i])
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
// TODO(bill): should this be an error or not?
|
||||
return true
|
||||
return true, nil
|
||||
|
||||
case reflect.Type_Info_Integer:
|
||||
i := strconv.parse_i128(str) or_return
|
||||
i, pok := strconv.parse_i128(str)
|
||||
if !pok {
|
||||
return false, nil
|
||||
}
|
||||
if assign_int(val, i) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
if assign_float(val, i) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
case reflect.Type_Info_Float:
|
||||
f := strconv.parse_f64(str) or_return
|
||||
f, pok := strconv.parse_f64(str)
|
||||
if !pok {
|
||||
return false, nil
|
||||
}
|
||||
if assign_int(val, f) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
if assign_float(val, f) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +323,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
case .Ident:
|
||||
advance_token(p)
|
||||
if p.spec == .MJSON {
|
||||
if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
|
||||
if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) or_return {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -312,13 +331,18 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
|
||||
case .String:
|
||||
advance_token(p)
|
||||
str := unquote_string(token, p.spec, p.allocator) or_return
|
||||
if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
|
||||
return nil
|
||||
str := unquote_string(token, p.spec, p.allocator) or_return
|
||||
dest := any{v.data, ti.id}
|
||||
if !(unmarshal_string_token(p, dest, str, ti) or_return) {
|
||||
delete(str, p.allocator)
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
delete(str, p.allocator)
|
||||
return UNSUPPORTED_TYPE
|
||||
|
||||
switch destv in dest {
|
||||
case string, cstring:
|
||||
case: delete(str, p.allocator)
|
||||
}
|
||||
return nil
|
||||
|
||||
case .Open_Brace:
|
||||
return unmarshal_object(p, v, .Close_Brace)
|
||||
|
||||
@@ -1531,7 +1531,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_LOWER)
|
||||
case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
|
||||
case:
|
||||
@@ -3002,18 +3002,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
fmt_bit_set(fi, v, verb = verb)
|
||||
|
||||
case runtime.Type_Info_Relative_Pointer:
|
||||
ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
|
||||
absolute_ptr := any{ptr, info.pointer.id}
|
||||
|
||||
fmt_value(fi, absolute_ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Relative_Multi_Pointer:
|
||||
ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
|
||||
absolute_ptr := any{ptr, info.pointer.id}
|
||||
|
||||
fmt_value(fi, absolute_ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
fmt_matrix(fi, v, verb, info)
|
||||
|
||||
|
||||
@@ -23,7 +23,15 @@ register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
loader := _internal_loaders[which(data)]
|
||||
if loader == nil {
|
||||
return nil, .Unsupported_Format
|
||||
|
||||
// Check if there is at least one loader, otherwise panic to let the user know about misuse.
|
||||
for a_loader in _internal_loaders {
|
||||
if a_loader != nil {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
}
|
||||
|
||||
panic("image.load called when no image loaders are registered. Register a loader by first importing a subpackage (eg: `import \"core:image/png\"`), or with image.register")
|
||||
}
|
||||
return loader(data, options, allocator)
|
||||
}
|
||||
@@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
|
||||
return .HDR
|
||||
case s[:4] == "\x38\x42\x50\x53":
|
||||
return .PSD
|
||||
case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
|
||||
case s[:4] == "\x53\x80\xF6\x34" && s[88:92] == "PICT":
|
||||
return .PIC
|
||||
case s[:4] == "\x69\x63\x6e\x73":
|
||||
return .ICNS
|
||||
|
||||
@@ -225,7 +225,7 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
|
||||
} else {
|
||||
write_byte(w, '\\', &n) or_return
|
||||
write_byte(w, 'U', &n) or_return
|
||||
for s := 24; s >= 0; s -= 4 {
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +53,15 @@ vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E)
|
||||
}
|
||||
@(require_results)
|
||||
quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
}
|
||||
@(require_results)
|
||||
quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
}
|
||||
@(require_results)
|
||||
quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
}
|
||||
|
||||
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
|
||||
|
||||
@@ -189,12 +189,12 @@ sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check {
|
||||
// sin coefficients
|
||||
@(private="file")
|
||||
_sin := [?]f64{
|
||||
0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10
|
||||
0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
|
||||
0h3ec71de3567d48a1, // 2.75573136213857245213e-6
|
||||
0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
|
||||
0h3f8111111110f7d0, // 8.33333333332211858878e-3
|
||||
0hbfc5555555555548, // -1.66666666666666307295e-1
|
||||
0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10
|
||||
0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
|
||||
0h3ec71de3567d48a1, // 2.75573136213857245213e-6
|
||||
0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
|
||||
0h3f8111111110f7d0, // 8.33333333332211858878e-3
|
||||
0hbfc5555555555548, // -1.66666666666666307295e-1
|
||||
}
|
||||
|
||||
// cos coefficients
|
||||
|
||||
@@ -670,20 +670,69 @@ choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
|
||||
|
||||
|
||||
@(require_results)
|
||||
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
|
||||
where
|
||||
intrinsics.type_is_enum(T),
|
||||
size_of(T) <= 8,
|
||||
len(T) == cap(T) /* Only allow contiguous enum types */ \
|
||||
{
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64(gen) % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T where intrinsics.type_is_enum(T) {
|
||||
when size_of(T) <= 8 && len(T) == cap(T) {
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64(gen) % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
} else {
|
||||
i := int63_max(i64(len(T)), gen)
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
}
|
||||
} else {
|
||||
i := int63_max(i64(len(T)), gen)
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
values := runtime.type_info_base(type_info_of(T)).variant.(runtime.Type_Info_Enum).values
|
||||
return T(choice(values))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a random *set* bit from the provided `bit_set`.
|
||||
|
||||
Inputs:
|
||||
- set: The `bit_set` to choose a random set bit from
|
||||
|
||||
Returns:
|
||||
- res: The randomly selected bit, or the zero value if `ok` is `false`
|
||||
- ok: Whether the bit_set was not empty and thus `res` is actually a random set bit
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
choice_bit_set_example :: proc() {
|
||||
Flags :: enum {
|
||||
A,
|
||||
B = 10,
|
||||
C,
|
||||
}
|
||||
|
||||
fmt.println(rand.choice_bit_set(bit_set[Flags]{}))
|
||||
fmt.println(rand.choice_bit_set(bit_set[Flags]{.B}))
|
||||
fmt.println(rand.choice_bit_set(bit_set[Flags]{.B, .C}))
|
||||
fmt.println(rand.choice_bit_set(bit_set[0..<15]{5, 1, 4}))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
A false
|
||||
B true
|
||||
C true
|
||||
5 true
|
||||
*/
|
||||
@(require_results)
|
||||
choice_bit_set :: proc(set: $T/bit_set[$E], gen := context.random_generator) -> (res: E, ok: bool) {
|
||||
total_set := card(set)
|
||||
if total_set == 0 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
core_set := transmute(intrinsics.type_bit_set_underlying_type(T))set
|
||||
|
||||
for target := int_max(total_set, gen); target > 0; target -= 1 {
|
||||
core_set &= core_set - 1
|
||||
}
|
||||
|
||||
return E(intrinsics.count_trailing_zeros(core_set)), true
|
||||
}
|
||||
|
||||
@@ -14,16 +14,16 @@ but an attempt to allocate memory is not an error.
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = nil_allocator_proc,
|
||||
data = nil,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
nil_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
return nil, nil
|
||||
@@ -41,7 +41,7 @@ not be allocated, and an attempt to allocate memory is an error.
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = panic_allocator_proc,
|
||||
data = nil,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,10 +157,10 @@ This procedure returns a pointer to the newly allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
arena_alloc :: proc(
|
||||
a: ^Arena,
|
||||
a: ^Arena,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := arena_alloc_bytes(a, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -175,10 +175,10 @@ This procedure returns a slice of the newly allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
arena_alloc_bytes :: proc(
|
||||
a: ^Arena,
|
||||
a: ^Arena,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
|
||||
if bytes != nil {
|
||||
@@ -197,10 +197,10 @@ memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
arena_alloc_non_zeroed :: proc(
|
||||
a: ^Arena,
|
||||
a: ^Arena,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -216,10 +216,10 @@ memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
arena_alloc_bytes_non_zeroed :: proc(
|
||||
a: ^Arena,
|
||||
a: ^Arena,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
if a.data == nil {
|
||||
panic("Arena is not initialized", loc)
|
||||
@@ -244,11 +244,11 @@ arena_free_all :: proc(a: ^Arena) {
|
||||
|
||||
arena_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size: int,
|
||||
alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
mode: Allocator_Mode,
|
||||
size: int,
|
||||
alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
arena := cast(^Arena)allocator_data
|
||||
@@ -398,10 +398,10 @@ returns a pointer to the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_alloc :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := scratch_alloc_bytes(s, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -416,10 +416,10 @@ returns a slice of the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_alloc_bytes :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
if bytes != nil {
|
||||
@@ -437,10 +437,10 @@ This procedure returns a pointer to the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_alloc_non_zeroed :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -455,10 +455,10 @@ This procedure returns a slice of the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_alloc_bytes_non_zeroed :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
if s.data == nil {
|
||||
DEFAULT_BACKING_SIZE :: 4 * Megabyte
|
||||
@@ -477,7 +477,7 @@ scratch_alloc_bytes_non_zeroed :: proc(
|
||||
offset = 0
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(offset+start, uintptr(alignment))
|
||||
ptr := align_forward_uintptr(offset+start, uintptr(alignment))
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
s.curr_offset = int(offset) + size
|
||||
return byte_slice(rawptr(ptr), size), nil
|
||||
@@ -574,12 +574,12 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_resize :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -603,11 +603,11 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_resize_bytes :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
|
||||
if bytes != nil && size > len(old_data) {
|
||||
@@ -634,12 +634,12 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_resize_non_zeroed :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -663,11 +663,11 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
scratch_resize_bytes_non_zeroed :: proc(
|
||||
s: ^Scratch,
|
||||
s: ^Scratch,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
old_memory := raw_data(old_data)
|
||||
old_size := len(old_data)
|
||||
@@ -678,8 +678,8 @@ scratch_resize_bytes_non_zeroed :: proc(
|
||||
}
|
||||
scratch_init(s, DEFAULT_BACKING_SIZE)
|
||||
}
|
||||
begin := uintptr(raw_data(s.data))
|
||||
end := begin + uintptr(len(s.data))
|
||||
begin := uintptr(raw_data(s.data))
|
||||
end := begin + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size
|
||||
@@ -695,11 +695,11 @@ scratch_resize_bytes_non_zeroed :: proc(
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
s := (^Scratch)(allocator_data)
|
||||
@@ -735,10 +735,10 @@ scratch_allocator_proc :: proc(
|
||||
Stack allocator data.
|
||||
*/
|
||||
Stack :: struct {
|
||||
data: []byte,
|
||||
data: []byte,
|
||||
prev_offset: int,
|
||||
curr_offset: int,
|
||||
peak_used: int,
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -769,7 +769,7 @@ previous allocation header.
|
||||
stack_allocator :: proc(stack: ^Stack) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = stack_allocator_proc,
|
||||
data = stack,
|
||||
data = stack,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,18 +780,18 @@ This procedure initializes the stack allocator with a backing buffer specified
|
||||
by `data` parameter.
|
||||
*/
|
||||
stack_init :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.stack_init'")
|
||||
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -803,10 +803,10 @@ procedure returns the pointer to the allocated memory.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_alloc :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := stack_alloc_bytes(s, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -821,10 +821,10 @@ procedure returns the slice of the allocated memory.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_alloc_bytes :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
if bytes != nil {
|
||||
@@ -842,10 +842,10 @@ zero-initialized. This procedure returns the pointer to the allocated memory.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_alloc_non_zeroed :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -860,10 +860,10 @@ zero-initialized. This procedure returns the slice of the allocated memory.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_alloc_bytes_non_zeroed :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location
|
||||
loc := #caller_location
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
if s.data == nil {
|
||||
panic("Stack allocation on an uninitialized stack allocator", loc)
|
||||
@@ -896,7 +896,7 @@ If the freeing does is an out of order freeing, the `.Invalid_Pointer` error
|
||||
is returned.
|
||||
*/
|
||||
stack_free :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
old_memory: rawptr,
|
||||
loc := #caller_location,
|
||||
) -> (Allocator_Error) {
|
||||
@@ -953,12 +953,12 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_resize :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment)
|
||||
return raw_data(bytes), err
|
||||
@@ -982,11 +982,11 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_resize_bytes :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
if bytes != nil {
|
||||
@@ -1017,12 +1017,12 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_resize_non_zeroed :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment)
|
||||
return raw_data(bytes), err
|
||||
@@ -1046,11 +1046,11 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
stack_resize_bytes_non_zeroed :: proc(
|
||||
s: ^Stack,
|
||||
s: ^Stack,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
old_memory := raw_data(old_data)
|
||||
old_size := len(old_data)
|
||||
@@ -1063,8 +1063,8 @@ stack_resize_bytes_non_zeroed :: proc(
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
curr_addr := uintptr(old_memory)
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)")
|
||||
@@ -1097,11 +1097,11 @@ stack_resize_bytes_non_zeroed :: proc(
|
||||
|
||||
stack_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size: int,
|
||||
alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
mode: Allocator_Mode,
|
||||
size: int,
|
||||
alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Stack)allocator_data
|
||||
@@ -1200,10 +1200,10 @@ returns a pointer to the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_alloc :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := small_stack_alloc_bytes(s, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -1218,10 +1218,10 @@ returns a slice of the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_alloc_bytes :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
if bytes != nil {
|
||||
@@ -1239,10 +1239,10 @@ procedure returns a pointer to the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_alloc_non_zeroed :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -1257,10 +1257,10 @@ procedure returns a slice of the allocated memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_alloc_bytes_non_zeroed :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
if s.data == nil {
|
||||
panic("Small stack is not initialized", loc)
|
||||
@@ -1289,7 +1289,7 @@ by `alignment`. The allocated memory is not explicitly zero-initialized. This
|
||||
procedure returns a slice of the allocated memory region.
|
||||
*/
|
||||
small_stack_free :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
old_memory: rawptr,
|
||||
loc := #caller_location,
|
||||
) -> Allocator_Error {
|
||||
@@ -1341,12 +1341,12 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_resize :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -1370,11 +1370,11 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_resize_bytes :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
|
||||
if bytes != nil {
|
||||
@@ -1405,12 +1405,12 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_resize_non_zeroed :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
|
||||
return raw_data(bytes), err
|
||||
@@ -1434,18 +1434,18 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
small_stack_resize_bytes_non_zeroed :: proc(
|
||||
s: ^Small_Stack,
|
||||
s: ^Small_Stack,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
loc := #caller_location,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
if s.data == nil {
|
||||
panic("Small stack is not initialized", loc)
|
||||
}
|
||||
old_memory := raw_data(old_data)
|
||||
old_size := len(old_data)
|
||||
alignment := alignment
|
||||
old_size := len(old_data)
|
||||
alignment := alignment
|
||||
alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
|
||||
if old_memory == nil {
|
||||
return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
@@ -1453,8 +1453,8 @@ small_stack_resize_bytes_non_zeroed :: proc(
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
curr_addr := uintptr(old_memory)
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
// panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
@@ -1476,11 +1476,11 @@ small_stack_resize_bytes_non_zeroed :: proc(
|
||||
}
|
||||
|
||||
small_stack_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data
|
||||
@@ -1514,17 +1514,17 @@ small_stack_allocator_proc :: proc(
|
||||
|
||||
|
||||
/* Preserved for compatibility */
|
||||
Dynamic_Pool :: Dynamic_Arena
|
||||
DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT
|
||||
Dynamic_Pool :: Dynamic_Arena
|
||||
DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT
|
||||
DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT
|
||||
dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc
|
||||
dynamic_pool_free_all :: dynamic_arena_free_all
|
||||
dynamic_pool_reset :: dynamic_arena_reset
|
||||
dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes
|
||||
dynamic_pool_alloc :: dynamic_arena_alloc
|
||||
dynamic_pool_init :: dynamic_arena_init
|
||||
dynamic_pool_allocator :: dynamic_arena_allocator
|
||||
dynamic_pool_destroy :: dynamic_arena_destroy
|
||||
dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc
|
||||
dynamic_pool_free_all :: dynamic_arena_free_all
|
||||
dynamic_pool_reset :: dynamic_arena_reset
|
||||
dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes
|
||||
dynamic_pool_alloc :: dynamic_arena_alloc
|
||||
dynamic_pool_init :: dynamic_arena_init
|
||||
dynamic_pool_allocator :: dynamic_arena_allocator
|
||||
dynamic_pool_destroy :: dynamic_arena_destroy
|
||||
|
||||
/*
|
||||
Default block size for dynamic arena.
|
||||
@@ -1540,16 +1540,16 @@ DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554
|
||||
Dynamic arena allocator data.
|
||||
*/
|
||||
Dynamic_Arena :: struct {
|
||||
block_size: int,
|
||||
out_band_size: int,
|
||||
alignment: int,
|
||||
unused_blocks: [dynamic]rawptr,
|
||||
used_blocks: [dynamic]rawptr,
|
||||
block_size: int,
|
||||
out_band_size: int,
|
||||
alignment: int,
|
||||
unused_blocks: [dynamic]rawptr,
|
||||
used_blocks: [dynamic]rawptr,
|
||||
out_band_allocations: [dynamic]rawptr,
|
||||
current_block: rawptr,
|
||||
current_pos: rawptr,
|
||||
bytes_left: int,
|
||||
block_allocator: Allocator,
|
||||
current_block: rawptr,
|
||||
current_pos: rawptr,
|
||||
bytes_left: int,
|
||||
block_allocator: Allocator,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1565,17 +1565,17 @@ dynamic_arena_init :: proc(
|
||||
pool: ^Dynamic_Arena,
|
||||
block_allocator := context.allocator,
|
||||
array_allocator := context.allocator,
|
||||
block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT,
|
||||
out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT,
|
||||
out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT,
|
||||
alignment := DEFAULT_ALIGNMENT,
|
||||
) {
|
||||
pool.block_size = block_size
|
||||
pool.out_band_size = out_band_size
|
||||
pool.alignment = alignment
|
||||
pool.block_allocator = block_allocator
|
||||
pool.block_size = block_size
|
||||
pool.out_band_size = out_band_size
|
||||
pool.alignment = alignment
|
||||
pool.block_allocator = block_allocator
|
||||
pool.out_band_allocations.allocator = array_allocator
|
||||
pool.unused_blocks.allocator = array_allocator
|
||||
pool.used_blocks.allocator = array_allocator
|
||||
pool.unused_blocks.allocator = array_allocator
|
||||
pool.used_blocks.allocator = array_allocator
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1783,10 +1783,10 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_arena_resize :: proc(
|
||||
a: ^Dynamic_Arena,
|
||||
a: ^Dynamic_Arena,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc)
|
||||
@@ -1811,9 +1811,9 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_arena_resize_bytes :: proc(
|
||||
a: ^Dynamic_Arena,
|
||||
a: ^Dynamic_Arena,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc)
|
||||
@@ -1845,10 +1845,10 @@ This procedure returns the pointer to the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_arena_resize_non_zeroed :: proc(
|
||||
a: ^Dynamic_Arena,
|
||||
a: ^Dynamic_Arena,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
size: int,
|
||||
old_size: int,
|
||||
size: int,
|
||||
loc := #caller_location,
|
||||
) -> (rawptr, Allocator_Error) {
|
||||
bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc)
|
||||
@@ -1873,9 +1873,9 @@ This procedure returns the slice of the resized memory region.
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_arena_resize_bytes_non_zeroed :: proc(
|
||||
a: ^Dynamic_Arena,
|
||||
a: ^Dynamic_Arena,
|
||||
old_data: []byte,
|
||||
size: int,
|
||||
size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
old_memory := raw_data(old_data)
|
||||
@@ -1892,11 +1892,11 @@ dynamic_arena_resize_bytes_non_zeroed :: proc(
|
||||
|
||||
dynamic_arena_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size: int,
|
||||
alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
mode: Allocator_Mode,
|
||||
size: int,
|
||||
alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
arena := (^Dynamic_Arena)(allocator_data)
|
||||
@@ -2048,7 +2048,7 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl
|
||||
// to pick the buddy as it "bounces around" less
|
||||
best_block = buddy
|
||||
}
|
||||
if (block.size <= buddy.size) {
|
||||
if block.size <= buddy.size {
|
||||
block = buddy_block_next(buddy)
|
||||
if (block < tail) {
|
||||
// Delay the buddy block for the next iteration
|
||||
@@ -2072,8 +2072,8 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl
|
||||
The buddy allocator data.
|
||||
*/
|
||||
Buddy_Allocator :: struct {
|
||||
head: ^Buddy_Block,
|
||||
tail: ^Buddy_Block,
|
||||
head: ^Buddy_Block,
|
||||
tail: ^Buddy_Block,
|
||||
alignment: uint,
|
||||
}
|
||||
|
||||
@@ -2234,11 +2234,11 @@ buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) {
|
||||
}
|
||||
|
||||
buddy_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
allocator_data: rawptr,
|
||||
mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
loc := #caller_location,
|
||||
) -> ([]byte, Allocator_Error) {
|
||||
b := (^Buddy_Allocator)(allocator_data)
|
||||
|
||||
@@ -461,10 +461,12 @@ Check if a pointer is aligned.
|
||||
|
||||
This procedure checks whether a pointer `x` is aligned to a boundary specified
|
||||
by `align`, and returns `true` if the pointer is aligned, and false otherwise.
|
||||
|
||||
The specified alignment must be a power of 2.
|
||||
*/
|
||||
is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool {
|
||||
p := uintptr(x)
|
||||
return (p & (1<<uintptr(align) - 1)) == 0
|
||||
return (p & (uintptr(align) - 1)) == 0
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#+build !freestanding
|
||||
#+build !freestanding, wasm32, wasm64p32
|
||||
package mem
|
||||
|
||||
import "core:sync"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#+build !freestanding
|
||||
#+build !freestanding, wasm32, wasm64p32
|
||||
package mem
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -6,17 +6,13 @@ import "core:sys/posix"
|
||||
|
||||
// Define non-posix needed flags:
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
|
||||
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
|
||||
|
||||
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
MAP_ANONYMOUS :: 0x1000
|
||||
|
||||
MADV_FREE :: 6
|
||||
MADV_FREE :: 6
|
||||
}
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
flags := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS)
|
||||
flags := posix.Map_Flags{ .ANONYMOUS, .PRIVATE }
|
||||
result := posix.mmap(nil, size, {}, flags)
|
||||
if result == posix.MAP_FAILED {
|
||||
return nil, .Out_Of_Memory
|
||||
|
||||
@@ -88,8 +88,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
||||
if res != nil {
|
||||
err = Dial_Error(os.is_platform_error(res) or_else -1)
|
||||
return
|
||||
close(skt)
|
||||
return {}, Dial_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -120,6 +120,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
|
||||
family := family_from_endpoint(interface_endpoint)
|
||||
sock := create_socket(family, .TCP) or_return
|
||||
skt = sock.(TCP_Socket)
|
||||
defer if err != nil { close(skt) }
|
||||
|
||||
// NOTE(tetra): This is so that if we crash while the socket is open, we can
|
||||
// bypass the cooldown period, and allow the next run of the program to
|
||||
|
||||
@@ -114,8 +114,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||
errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
|
||||
if errno != nil {
|
||||
err = cast(Dial_Error)errno
|
||||
return
|
||||
close(socket)
|
||||
return {}, cast(Dial_Error)errno
|
||||
}
|
||||
|
||||
return
|
||||
@@ -137,6 +137,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
|
||||
family := family_from_endpoint(interface_endpoint)
|
||||
new_socket := create_socket(family, .TCP) or_return
|
||||
socket = new_socket.(TCP_Socket)
|
||||
defer if err != nil { close(socket) }
|
||||
|
||||
bind(socket, interface_endpoint) or_return
|
||||
|
||||
|
||||
@@ -147,7 +147,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
addr := _unwrap_os_addr(endpoint)
|
||||
errno = linux.connect(linux.Fd(os_sock), &addr)
|
||||
if errno != .NONE {
|
||||
return cast(TCP_Socket) os_sock, Dial_Error(errno)
|
||||
close(cast(TCP_Socket) os_sock)
|
||||
return {}, Dial_Error(errno)
|
||||
}
|
||||
// NOTE(tetra): Not vital to succeed; error ignored
|
||||
no_delay: b32 = cast(b32) options.no_delay
|
||||
@@ -166,40 +167,48 @@ _bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network_Error) {
|
||||
_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
|
||||
errno: linux.Errno
|
||||
assert(backlog > 0 && i32(backlog) < max(i32))
|
||||
|
||||
// Figure out the address family and address of the endpoint
|
||||
ep_family := _unwrap_os_family(family_from_endpoint(endpoint))
|
||||
ep_address := _unwrap_os_addr(endpoint)
|
||||
|
||||
// Create TCP socket
|
||||
os_sock: linux.Fd
|
||||
os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
|
||||
if errno != .NONE {
|
||||
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
||||
return {}, Create_Socket_Error(errno)
|
||||
err = Create_Socket_Error(errno)
|
||||
return
|
||||
}
|
||||
socket = cast(TCP_Socket)os_sock
|
||||
defer if err != nil { close(socket) }
|
||||
|
||||
// NOTE(tetra): This is so that if we crash while the socket is open, we can
|
||||
// bypass the cooldown period, and allow the next run of the program to
|
||||
// use the same address immediately.
|
||||
//
|
||||
// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
|
||||
do_reuse_addr: b32 = true
|
||||
errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr)
|
||||
if errno != .NONE {
|
||||
return cast(TCP_Socket) os_sock, Listen_Error(errno)
|
||||
if errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr); errno != .NONE {
|
||||
err = Listen_Error(errno)
|
||||
return
|
||||
}
|
||||
|
||||
// Bind the socket to endpoint address
|
||||
errno = linux.bind(os_sock, &ep_address)
|
||||
if errno != .NONE {
|
||||
return cast(TCP_Socket) os_sock, Bind_Error(errno)
|
||||
if errno = linux.bind(os_sock, &ep_address); errno != .NONE {
|
||||
err = Bind_Error(errno)
|
||||
return
|
||||
}
|
||||
|
||||
// Listen on bound socket
|
||||
errno = linux.listen(os_sock, cast(i32) backlog)
|
||||
if errno != .NONE {
|
||||
return cast(TCP_Socket) os_sock, Listen_Error(errno)
|
||||
if errno = linux.listen(os_sock, cast(i32) backlog); errno != .NONE {
|
||||
err = Listen_Error(errno)
|
||||
return
|
||||
}
|
||||
return cast(TCP_Socket) os_sock, nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -80,8 +80,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||
res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
|
||||
if res < 0 {
|
||||
err = Dial_Error(win.WSAGetLastError())
|
||||
return
|
||||
close(socket)
|
||||
return {}, Dial_Error(win.WSAGetLastError())
|
||||
}
|
||||
|
||||
if options.no_delay {
|
||||
@@ -107,6 +107,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
|
||||
family := family_from_endpoint(interface_endpoint)
|
||||
sock := create_socket(family, .TCP) or_return
|
||||
socket = sock.(TCP_Socket)
|
||||
defer if err != nil { close(socket) }
|
||||
|
||||
// NOTE(tetra): While I'm not 100% clear on it, my understanding is that this will
|
||||
// prevent hijacking of the server's endpoint by other applications.
|
||||
|
||||
@@ -776,17 +776,18 @@ Dynamic_Array_Type :: struct {
|
||||
|
||||
Struct_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
field_align: ^Expr,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
is_no_copy: bool,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
min_field_align: ^Expr,
|
||||
max_field_align: ^Expr,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
is_no_copy: bool,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
}
|
||||
|
||||
Union_Type_Kind :: enum u8 {
|
||||
|
||||
@@ -316,7 +316,8 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case ^Struct_Type:
|
||||
r.poly_params = auto_cast clone(r.poly_params)
|
||||
r.align = clone(r.align)
|
||||
r.field_align = clone(r.field_align)
|
||||
r.min_field_align = clone(r.min_field_align)
|
||||
r.max_field_align = clone(r.max_field_align)
|
||||
r.fields = auto_cast clone(r.fields)
|
||||
case ^Union_Type:
|
||||
r.poly_params = auto_cast clone(r.poly_params)
|
||||
|
||||
@@ -2610,9 +2610,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
case .Struct:
|
||||
tok := expect_token(p, .Struct)
|
||||
|
||||
poly_params: ^ast.Field_List
|
||||
align: ^ast.Expr
|
||||
field_align: ^ast.Expr
|
||||
poly_params: ^ast.Field_List
|
||||
align: ^ast.Expr
|
||||
min_field_align: ^ast.Expr
|
||||
max_field_align: ^ast.Expr
|
||||
is_packed: bool
|
||||
is_raw_union: bool
|
||||
is_no_copy: bool
|
||||
@@ -2645,10 +2646,21 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
align = parse_expr(p, true)
|
||||
case "field_align":
|
||||
if field_align != nil {
|
||||
if min_field_align != nil {
|
||||
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
|
||||
}
|
||||
field_align = parse_expr(p, true)
|
||||
warn(p, tag.pos, "#field_align has been deprecated in favour of #min_field_align")
|
||||
min_field_align = parse_expr(p, true)
|
||||
case "min_field_align":
|
||||
if min_field_align != nil {
|
||||
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
|
||||
}
|
||||
min_field_align = parse_expr(p, true)
|
||||
case "max_field_align":
|
||||
if max_field_align != nil {
|
||||
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
|
||||
}
|
||||
max_field_align = parse_expr(p, true)
|
||||
case "raw_union":
|
||||
if is_raw_union {
|
||||
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
|
||||
@@ -2689,16 +2701,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
close := expect_closing_brace_of_field_list(p)
|
||||
|
||||
st := ast.new(ast.Struct_Type, tok.pos, end_pos(close))
|
||||
st.poly_params = poly_params
|
||||
st.align = align
|
||||
st.field_align = field_align
|
||||
st.is_packed = is_packed
|
||||
st.is_raw_union = is_raw_union
|
||||
st.is_no_copy = is_no_copy
|
||||
st.fields = fields
|
||||
st.name_count = name_count
|
||||
st.where_token = where_token
|
||||
st.where_clauses = where_clauses
|
||||
st.poly_params = poly_params
|
||||
st.align = align
|
||||
st.min_field_align = min_field_align
|
||||
st.max_field_align = max_field_align
|
||||
st.is_packed = is_packed
|
||||
st.is_raw_union = is_raw_union
|
||||
st.is_no_copy = is_no_copy
|
||||
st.fields = fields
|
||||
st.name_count = name_count
|
||||
st.where_token = where_token
|
||||
st.where_clauses = where_clauses
|
||||
return st
|
||||
|
||||
case .Union:
|
||||
@@ -3683,6 +3696,8 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
|
||||
}
|
||||
}
|
||||
|
||||
end := p.prev_tok
|
||||
|
||||
if p.expr_level >= 0 {
|
||||
end: ^ast.Expr
|
||||
if !is_mutable && len(values) > 0 {
|
||||
@@ -3702,7 +3717,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
|
||||
}
|
||||
}
|
||||
|
||||
decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok))
|
||||
decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(end))
|
||||
decl.docs = docs
|
||||
decl.names = names
|
||||
decl.type = type
|
||||
|
||||
@@ -331,7 +331,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
if x > max || 0xd800 <= x && x <= 0xdfff {
|
||||
error(t, offset, "escape sequence is an invalid Unicode code point")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_locati
|
||||
|
||||
@(deferred_out=TEMP_ALLOCATOR_GUARD_END)
|
||||
TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
|
||||
tmp := temp_allocator_temp_begin(loc)
|
||||
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
|
||||
tmp := temp_allocator_temp_begin(loc)
|
||||
return tmp, loc
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
return {}, nil
|
||||
return {}, .Unsupported
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
|
||||
@@ -16,28 +16,25 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
|
||||
}
|
||||
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
|
||||
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
|
||||
#assert(size_of(fi.inode) == 16)
|
||||
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
return
|
||||
}
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
delete(it.impl.path, file_allocator())
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
|
||||
@@ -162,6 +162,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
|
||||
return .Invalid_File
|
||||
case .ENOMEM:
|
||||
return .Out_Of_Memory
|
||||
case .ENOSYS:
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
return Platform_Error(i32(errno))
|
||||
|
||||
@@ -26,6 +26,8 @@ _get_platform_error :: proc() -> Error {
|
||||
return .Invalid_File
|
||||
case .ENOMEM:
|
||||
return .Out_Of_Memory
|
||||
case .ENOSYS:
|
||||
return .Unsupported
|
||||
case:
|
||||
return Platform_Error(errno)
|
||||
}
|
||||
|
||||
@@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error {
|
||||
case win32.ERROR_NEGATIVE_SEEK:
|
||||
return .Invalid_Offset
|
||||
|
||||
case win32.ERROR_BROKEN_PIPE:
|
||||
return .Broken_Pipe
|
||||
|
||||
case
|
||||
win32.ERROR_BAD_ARGUMENTS,
|
||||
win32.ERROR_INVALID_PARAMETER,
|
||||
win32.ERROR_NOT_ENOUGH_MEMORY,
|
||||
win32.ERROR_NO_MORE_FILES,
|
||||
win32.ERROR_LOCK_VIOLATION,
|
||||
win32.ERROR_BROKEN_PIPE,
|
||||
win32.ERROR_CALL_NOT_IMPLEMENTED,
|
||||
win32.ERROR_INSUFFICIENT_BUFFER,
|
||||
win32.ERROR_INVALID_NAME,
|
||||
|
||||
@@ -164,7 +164,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error {
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error {
|
||||
flags := O_WRONLY|O_CREATE
|
||||
if truncate {
|
||||
flags |= O_TRUNC
|
||||
|
||||
@@ -50,9 +50,9 @@ init_std_files :: proc() {
|
||||
}
|
||||
@(fini)
|
||||
fini_std_files :: proc() {
|
||||
close(stdin)
|
||||
close(stdout)
|
||||
close(stderr)
|
||||
_destroy((^File_Impl)(stdin.impl))
|
||||
_destroy((^File_Impl)(stdout.impl))
|
||||
_destroy((^File_Impl)(stderr.impl))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
|
||||
internal_mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
dir, file := filepath.split(path)
|
||||
if file != path {
|
||||
if file != path && dir != "/" {
|
||||
if len(dir) > 1 && dir[len(dir) - 1] == '/' {
|
||||
dir = dir[:len(dir) - 1]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
package os2
|
||||
|
||||
/*
|
||||
Create an anonymous pipe.
|
||||
|
||||
This procedure creates an anonymous pipe, returning two ends of the pipe, `r`
|
||||
and `w`. The file `r` is the readable end of the pipe. The file `w` is a
|
||||
writeable end of the pipe.
|
||||
|
||||
Pipes are used as an inter-process communication mechanism, to communicate
|
||||
between a parent and a child process. The child uses one end of the pipe to
|
||||
write data, and the parent uses the other end to read from the pipe
|
||||
(or vice-versa). When a parent passes one of the ends of the pipe to the child
|
||||
process, that end of the pipe needs to be closed by the parent, before any data
|
||||
is attempted to be read.
|
||||
|
||||
Although pipes look like files and is compatible with most file APIs in package
|
||||
os2, the way it's meant to be read is different. Due to asynchronous nature of
|
||||
the communication channel, the data may not be present at the time of a read
|
||||
request. The other scenario is when a pipe has no data because the other end
|
||||
of the pipe was closed by the child process.
|
||||
*/
|
||||
@(require_results)
|
||||
pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return _pipe()
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the pipe has any data.
|
||||
|
||||
This procedure checks whether a read-end of the pipe has data that can be
|
||||
read, and returns `true`, if the pipe has readable data, and `false` if the
|
||||
pipe is empty. This procedure does not block the execution of the current
|
||||
thread.
|
||||
|
||||
**Note**: If the other end of the pipe was closed by the child process, the
|
||||
`.Broken_Pipe`
|
||||
can be returned by this procedure. Handle these errors accordingly.
|
||||
*/
|
||||
@(require_results)
|
||||
pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
return _pipe_has_data(r)
|
||||
}
|
||||
|
||||
@@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
if r == nil || r.impl == nil {
|
||||
return false, nil
|
||||
}
|
||||
fd := linux.Fd((^File_Impl)(r.impl).fd)
|
||||
poll_fds := []linux.Poll_Fd {
|
||||
linux.Poll_Fd {
|
||||
fd = fd,
|
||||
events = {.IN, .HUP},
|
||||
},
|
||||
}
|
||||
n, errno := linux.poll(poll_fds, 0)
|
||||
if n != 1 || errno != nil {
|
||||
return false, _get_platform_error(errno)
|
||||
}
|
||||
pipe_events := poll_fds[0].revents
|
||||
if pipe_events >= {.IN} {
|
||||
return true, nil
|
||||
}
|
||||
if pipe_events >= {.HUP} {
|
||||
return false, .Broken_Pipe
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
@@ -44,3 +44,30 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
if r == nil || r.impl == nil {
|
||||
return false, nil
|
||||
}
|
||||
fd := __fd(r)
|
||||
poll_fds := []posix.pollfd {
|
||||
posix.pollfd {
|
||||
fd = fd,
|
||||
events = {.IN, .HUP},
|
||||
},
|
||||
}
|
||||
n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0)
|
||||
if n < 0 {
|
||||
return false, _get_platform_error()
|
||||
} else if n != 1 {
|
||||
return false, nil
|
||||
}
|
||||
pipe_events := poll_fds[0].revents
|
||||
if pipe_events >= {.IN} {
|
||||
return true, nil
|
||||
}
|
||||
if pipe_events >= {.HUP} {
|
||||
return false, .Broken_Pipe
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
if r == nil || r.impl == nil {
|
||||
return false, nil
|
||||
}
|
||||
handle := win32.HANDLE((^File_Impl)(r.impl).fd)
|
||||
bytes_available: u32
|
||||
if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) {
|
||||
return false, _get_platform_error()
|
||||
}
|
||||
return bytes_available > 0, nil
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:time"
|
||||
|
||||
/*
|
||||
In procedures that explicitly state this as one of the allowed values,
|
||||
specifies an infinite timeout.
|
||||
In procedures that explicitly state this as one of the allowed values,
|
||||
specifies an infinite timeout.
|
||||
*/
|
||||
TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
|
||||
|
||||
/*
|
||||
Arguments to the current process.
|
||||
Arguments to the current process.
|
||||
*/
|
||||
args := get_args()
|
||||
|
||||
@@ -24,17 +25,17 @@ get_args :: proc() -> []string {
|
||||
}
|
||||
|
||||
/*
|
||||
Exit the current process.
|
||||
Exit the current process.
|
||||
*/
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
_exit(code)
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the UID of the current process.
|
||||
Obtain the UID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_uid :: proc() -> int {
|
||||
@@ -42,15 +43,15 @@ get_uid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the effective UID of the current process.
|
||||
Obtain the effective UID of the current process.
|
||||
|
||||
The effective UID is typically the same as the UID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real UID of the process and the effective UID are different.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
The effective UID is typically the same as the UID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real UID of the process and the effective UID are different.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_euid :: proc() -> int {
|
||||
@@ -58,10 +59,10 @@ get_euid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the GID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
Obtain the GID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_gid :: proc() -> int {
|
||||
@@ -69,15 +70,15 @@ get_gid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the effective GID of the current process.
|
||||
|
||||
The effective GID is typically the same as the GID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real GID of the process and the effective GID are different.
|
||||
Obtain the effective GID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
The effective GID is typically the same as the GID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real GID of the process and the effective GID are different.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_egid :: proc() -> int {
|
||||
@@ -85,7 +86,7 @@ get_egid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the ID of the current process.
|
||||
Obtain the ID of the current process.
|
||||
*/
|
||||
@(require_results)
|
||||
get_pid :: proc() -> int {
|
||||
@@ -93,13 +94,13 @@ get_pid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the ID of the parent process.
|
||||
Obtain the ID of the parent process.
|
||||
|
||||
**Note(windows)**: Windows does not mantain strong relationships between
|
||||
parent and child processes. This function returns the ID of the process
|
||||
that has created the current process. In case the parent has died, the ID
|
||||
returned by this function can identify a non-existent or a different
|
||||
process.
|
||||
**Note(windows)**: Windows does not mantain strong relationships between
|
||||
parent and child processes. This function returns the ID of the process
|
||||
that has created the current process. In case the parent has died, the ID
|
||||
returned by this function can identify a non-existent or a different
|
||||
process.
|
||||
*/
|
||||
@(require_results)
|
||||
get_ppid :: proc() -> int {
|
||||
@@ -107,7 +108,7 @@ get_ppid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain ID's of all processes running in the system.
|
||||
Obtain ID's of all processes running in the system.
|
||||
*/
|
||||
@(require_results)
|
||||
process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
|
||||
@@ -115,9 +116,9 @@ process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
|
||||
}
|
||||
|
||||
/*
|
||||
Bit set specifying which fields of the `Process_Info` struct need to be
|
||||
obtained by the `process_info()` procedure. Each bit corresponds to a
|
||||
field in the `Process_Info` struct.
|
||||
Bit set specifying which fields of the `Process_Info` struct need to be
|
||||
obtained by the `process_info()` procedure. Each bit corresponds to a
|
||||
field in the `Process_Info` struct.
|
||||
*/
|
||||
Process_Info_Fields :: bit_set[Process_Info_Field]
|
||||
Process_Info_Field :: enum {
|
||||
@@ -134,8 +135,8 @@ Process_Info_Field :: enum {
|
||||
ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
|
||||
|
||||
/*
|
||||
Contains information about the process as obtained by the `process_info()`
|
||||
procedure.
|
||||
Contains information about the process as obtained by the `process_info()`
|
||||
procedure.
|
||||
*/
|
||||
Process_Info :: struct {
|
||||
// The information about a process the struct contains. `pid` is always
|
||||
@@ -162,19 +163,19 @@ Process_Info :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about a process.
|
||||
Obtain information about a process.
|
||||
|
||||
This procedure obtains an information, specified by `selection` parameter of
|
||||
a process given by `pid`.
|
||||
This procedure obtains an information, specified by `selection` parameter of
|
||||
a process given by `pid`.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -182,20 +183,20 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about a process.
|
||||
Obtain information about a process.
|
||||
|
||||
This procedure obtains information, specified by `selection` parameter
|
||||
about a process that has been opened by the application, specified in
|
||||
the `process` parameter.
|
||||
This procedure obtains information, specified by `selection` parameter
|
||||
about a process that has been opened by the application, specified in
|
||||
the `process` parameter.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -203,19 +204,19 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about the current process.
|
||||
Obtain information about the current process.
|
||||
|
||||
This procedure obtains the information, specified by `selection` parameter
|
||||
about the currently running process.
|
||||
This procedure obtains the information, specified by `selection` parameter
|
||||
about the currently running process.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -223,7 +224,7 @@ current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about the specified process.
|
||||
Obtain information about the specified process.
|
||||
*/
|
||||
process_info :: proc {
|
||||
process_info_by_pid,
|
||||
@@ -232,11 +233,11 @@ process_info :: proc {
|
||||
}
|
||||
|
||||
/*
|
||||
Free the information about the process.
|
||||
Free the information about the process.
|
||||
|
||||
This procedure frees the memory occupied by process info using the provided
|
||||
allocator. The allocator needs to be the same allocator that was supplied
|
||||
to the `process_info` function.
|
||||
This procedure frees the memory occupied by process info using the provided
|
||||
allocator. The allocator needs to be the same allocator that was supplied
|
||||
to the `process_info` function.
|
||||
*/
|
||||
free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
|
||||
delete(pi.executable_path, allocator)
|
||||
@@ -254,13 +255,13 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
|
||||
}
|
||||
|
||||
/*
|
||||
Represents a process handle.
|
||||
Represents a process handle.
|
||||
|
||||
When a process dies, the OS is free to re-use the pid of that process. The
|
||||
`Process` struct represents a handle to the process that will refer to a
|
||||
specific process, even after it has died.
|
||||
When a process dies, the OS is free to re-use the pid of that process. The
|
||||
`Process` struct represents a handle to the process that will refer to a
|
||||
specific process, even after it has died.
|
||||
|
||||
**Note(linux)**: The `handle` will be referring to pidfd.
|
||||
**Note(linux)**: The `handle` will be referring to pidfd.
|
||||
*/
|
||||
Process :: struct {
|
||||
pid: int,
|
||||
@@ -276,13 +277,13 @@ Process_Open_Flag :: enum {
|
||||
}
|
||||
|
||||
/*
|
||||
Open a process handle using it's pid.
|
||||
Open a process handle using it's pid.
|
||||
|
||||
This procedure obtains a process handle of a process specified by `pid`.
|
||||
This procedure can be subject to race conditions. See the description of
|
||||
`Process`.
|
||||
This procedure obtains a process handle of a process specified by `pid`.
|
||||
This procedure can be subject to race conditions. See the description of
|
||||
`Process`.
|
||||
|
||||
Use `process_close()` function to close the process handle.
|
||||
Use `process_close()` function to close the process handle.
|
||||
*/
|
||||
@(require_results)
|
||||
process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
|
||||
@@ -322,31 +323,139 @@ Process_Desc :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Create a new process and obtain its handle.
|
||||
Create a new process and obtain its handle.
|
||||
|
||||
This procedure creates a new process, with a given command and environment
|
||||
strings as parameters. Use `environ()` to inherit the environment of the
|
||||
current process.
|
||||
This procedure creates a new process, with a given command and environment
|
||||
strings as parameters. Use `environ()` to inherit the environment of the
|
||||
current process.
|
||||
|
||||
The `desc` parameter specifies the description of how the process should
|
||||
be created. It contains information such as the command line, the
|
||||
environment of the process, the starting directory and many other options.
|
||||
Most of the fields in the struct can be set to `nil` or an empty value.
|
||||
|
||||
Use `process_close` to close the handle to the process. Note, that this
|
||||
is not the same as terminating the process. One can terminate the process
|
||||
and not close the handle, in which case the handle would be leaked. In case
|
||||
the function returns an error, an invalid handle is returned.
|
||||
The `desc` parameter specifies the description of how the process should
|
||||
be created. It contains information such as the command line, the
|
||||
environment of the process, the starting directory and many other options.
|
||||
Most of the fields in the struct can be set to `nil` or an empty value.
|
||||
|
||||
This procedure is not thread-safe. It may alter the inheritance properties
|
||||
of file handles in an unpredictable manner. In case multiple threads change
|
||||
handle inheritance properties, make sure to serialize all those calls.
|
||||
Use `process_close` to close the handle to the process. Note, that this
|
||||
is not the same as terminating the process. One can terminate the process
|
||||
and not close the handle, in which case the handle would be leaked. In case
|
||||
the function returns an error, an invalid handle is returned.
|
||||
|
||||
This procedure is not thread-safe. It may alter the inheritance properties
|
||||
of file handles in an unpredictable manner. In case multiple threads change
|
||||
handle inheritance properties, make sure to serialize all those calls.
|
||||
*/
|
||||
@(require_results)
|
||||
process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
|
||||
process_start :: proc(desc: Process_Desc) -> (Process, Error) {
|
||||
return _process_start(desc)
|
||||
}
|
||||
|
||||
/*
|
||||
Execute the process and capture stdout and stderr streams.
|
||||
|
||||
This procedure creates a new process, with a given command and environment
|
||||
strings as parameters, and waits until the process finishes execution. While
|
||||
the process is running, this procedure accumulates the output of its stdout
|
||||
and stderr streams and returns byte slices containing the captured data from
|
||||
the streams.
|
||||
|
||||
This procedure expects that `stdout` and `stderr` fields of the `desc` parameter
|
||||
are left at default, i.e. a `nil` value. You can not capture stdout/stderr and
|
||||
redirect it to a file at the same time.
|
||||
|
||||
This procedure does not free `stdout` and `stderr` slices before an error is
|
||||
returned. Make sure to call `delete` on these slices.
|
||||
*/
|
||||
@(require_results)
|
||||
process_exec :: proc(
|
||||
desc: Process_Desc,
|
||||
allocator: runtime.Allocator,
|
||||
loc := #caller_location,
|
||||
) -> (
|
||||
state: Process_State,
|
||||
stdout: []byte,
|
||||
stderr: []byte,
|
||||
err: Error,
|
||||
) {
|
||||
assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
|
||||
assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
|
||||
|
||||
stdout_r, stdout_w := pipe() or_return
|
||||
defer close(stdout_r)
|
||||
stderr_r, stderr_w := pipe() or_return
|
||||
defer close(stderr_r)
|
||||
|
||||
process: Process
|
||||
{
|
||||
// NOTE(flysand): Make sure the write-ends are closed, regardless
|
||||
// of the outcome. This makes read-ends readable on our side.
|
||||
defer close(stdout_w)
|
||||
defer close(stderr_w)
|
||||
desc := desc
|
||||
desc.stdout = stdout_w
|
||||
desc.stderr = stderr_w
|
||||
process = process_start(desc) or_return
|
||||
}
|
||||
|
||||
{
|
||||
stdout_b: [dynamic]byte
|
||||
stdout_b.allocator = allocator
|
||||
defer stdout = stdout_b[:]
|
||||
|
||||
stderr_b: [dynamic]byte
|
||||
stderr_b.allocator = allocator
|
||||
defer stderr = stderr_b[:]
|
||||
|
||||
buf: [1024]u8 = ---
|
||||
|
||||
stdout_done, stderr_done, has_data: bool
|
||||
for err == nil && (!stdout_done || !stderr_done) {
|
||||
n := 0
|
||||
|
||||
if !stdout_done {
|
||||
has_data, err = pipe_has_data(stdout_r)
|
||||
if has_data {
|
||||
n, err = read(stdout_r, buf[:])
|
||||
}
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
_, err = append(&stdout_b, ..buf[:n])
|
||||
case .EOF, .Broken_Pipe:
|
||||
stdout_done = true
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && !stderr_done {
|
||||
n = 0
|
||||
has_data, err = pipe_has_data(stderr_r)
|
||||
if has_data {
|
||||
n, err = read(stderr_r, buf[:])
|
||||
}
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
_, err = append(&stderr_b, ..buf[:n])
|
||||
case .EOF, .Broken_Pipe:
|
||||
stderr_done = true
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
state, _ = process_wait(process, timeout=0)
|
||||
if !state.exited {
|
||||
_ = process_kill(process)
|
||||
state, _ = process_wait(process)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
state, err = process_wait(process)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
The state of the process after it has finished execution.
|
||||
*/
|
||||
@@ -371,17 +480,17 @@ Process_State :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Wait for a process event.
|
||||
Wait for a process event.
|
||||
|
||||
This procedure blocks the execution until the process has exited or the
|
||||
timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
|
||||
no timeout restriction is imposed and the procedure can block indefinately.
|
||||
This procedure blocks the execution until the process has exited or the
|
||||
timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
|
||||
no timeout restriction is imposed and the procedure can block indefinately.
|
||||
|
||||
If the timeout has expired, the `General_Error.Timeout` is returned as
|
||||
the error.
|
||||
If the timeout has expired, the `General_Error.Timeout` is returned as
|
||||
the error.
|
||||
|
||||
If an error is returned for any other reason, other than timeout, the
|
||||
process state is considered undetermined.
|
||||
If an error is returned for any other reason, other than timeout, the
|
||||
process state is considered undetermined.
|
||||
*/
|
||||
@(require_results)
|
||||
process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
|
||||
@@ -389,12 +498,12 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_
|
||||
}
|
||||
|
||||
/*
|
||||
Close the handle to a process.
|
||||
Close the handle to a process.
|
||||
|
||||
This procedure closes the handle associated with a process. It **does not**
|
||||
terminate a process, in case it was running. In case a termination is
|
||||
desired, kill the process first, wait for the process to finish,
|
||||
then close the handle.
|
||||
This procedure closes the handle associated with a process. It **does not**
|
||||
terminate a process, in case it was running. In case a termination is
|
||||
desired, kill the process first, wait for the process to finish,
|
||||
then close the handle.
|
||||
*/
|
||||
@(require_results)
|
||||
process_close :: proc(process: Process) -> (Error) {
|
||||
@@ -402,10 +511,9 @@ process_close :: proc(process: Process) -> (Error) {
|
||||
}
|
||||
|
||||
/*
|
||||
Terminate a process.
|
||||
|
||||
This procedure terminates a process, specified by it's handle, `process`.
|
||||
Terminate a process.
|
||||
|
||||
This procedure terminates a process, specified by it's handle, `process`.
|
||||
*/
|
||||
@(require_results)
|
||||
process_kill :: proc(process: Process) -> (Error) {
|
||||
|
||||
@@ -523,7 +523,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! {
|
||||
error_byte: [1]u8 = { u8(errno) }
|
||||
linux.write(parent_fd, error_byte[:])
|
||||
intrinsics.trap()
|
||||
linux.exit(126)
|
||||
}
|
||||
|
||||
stdin_fd: linux.Fd
|
||||
|
||||
@@ -163,7 +163,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
#assert(len(posix.Errno) < max(u8))
|
||||
errno := u8(posix.errno())
|
||||
posix.write(parent_fd, &errno, 1)
|
||||
runtime.trap()
|
||||
posix.exit(126)
|
||||
}
|
||||
|
||||
null := posix.open("/dev/null", {.RDWR})
|
||||
@@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
process.pid = int(pid)
|
||||
process, _ = _process_open(int(pid), {})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error)
|
||||
}
|
||||
|
||||
_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
|
||||
process.pid = pid
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
|
||||
}
|
||||
if desc.stdin != nil {
|
||||
stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
|
||||
stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd)
|
||||
}
|
||||
|
||||
working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
|
||||
@@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) ->
|
||||
strings.write_byte(&builder, ' ')
|
||||
}
|
||||
j := 0
|
||||
strings.write_byte(&builder, '"')
|
||||
for j < len(arg) {
|
||||
backslashes := 0
|
||||
for j < len(arg) && arg[j] == '\\' {
|
||||
backslashes += 1
|
||||
if strings.contains_any(arg, "()[]{}^=;!'+,`~\" ") {
|
||||
strings.write_byte(&builder, '"')
|
||||
for j < len(arg) {
|
||||
backslashes := 0
|
||||
for j < len(arg) && arg[j] == '\\' {
|
||||
backslashes += 1
|
||||
j += 1
|
||||
}
|
||||
if j == len(arg) {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes)
|
||||
break
|
||||
} else if arg[j] == '"' {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes+1)
|
||||
strings.write_byte(&builder, arg[j])
|
||||
} else {
|
||||
_write_byte_n_times(&builder, '\\', backslashes)
|
||||
strings.write_byte(&builder, arg[j])
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
if j == len(arg) {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes)
|
||||
break
|
||||
} else if arg[j] == '"' {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes+1)
|
||||
strings.write_byte(&builder, '"')
|
||||
} else {
|
||||
_write_byte_n_times(&builder, '\\', backslashes)
|
||||
strings.write_byte(&builder, arg[j])
|
||||
}
|
||||
j += 1
|
||||
strings.write_byte(&builder, '"')
|
||||
} else {
|
||||
strings.write_string(&builder, arg)
|
||||
}
|
||||
strings.write_byte(&builder, '"')
|
||||
}
|
||||
return strings.to_string(builder)
|
||||
}
|
||||
|
||||
@@ -200,22 +200,21 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi
|
||||
} else {
|
||||
mode |= 0o666
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
is_sym = false
|
||||
} else {
|
||||
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
|
||||
if is_sym {
|
||||
type = .Symlink
|
||||
} else {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
}
|
||||
if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
} else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
} else if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1061,7 +1061,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -1076,9 +1076,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool {
|
||||
|
||||
@@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := cstring(path_ptr)
|
||||
path = strings.clone(string(path_cstr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_cstr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -242,10 +242,13 @@ F_SETFL: int : 4 /* Set file flags */
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001
|
||||
RTLD_NOW :: 0x002
|
||||
RTLD_BINDING_MASK :: 0x3
|
||||
RTLD_GLOBAL :: 0x100
|
||||
RTLD_LAZY :: 0x0001
|
||||
RTLD_NOW :: 0x0002
|
||||
RTLD_BINDING_MASK :: 0x0003
|
||||
RTLD_GLOBAL :: 0x0100
|
||||
RTLD_NOLOAD :: 0x0004
|
||||
RTLD_DEEPBIND :: 0x0008
|
||||
RTLD_NODELETE :: 0x1000
|
||||
|
||||
socklen_t :: c.int
|
||||
|
||||
@@ -487,7 +490,7 @@ foreign libc {
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||
|
||||
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int ---
|
||||
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> c.int ---
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
|
||||
@(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
|
||||
@@ -914,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -929,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -246,6 +246,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
|
||||
if err != .None {
|
||||
return
|
||||
}
|
||||
defer {
|
||||
for s in m {
|
||||
delete(s)
|
||||
}
|
||||
delete(m)
|
||||
}
|
||||
|
||||
dmatches := make([dynamic]string, 0, 0)
|
||||
for d in m {
|
||||
dmatches, err = _glob(d, file, &dmatches)
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#+build linux, darwin, freebsd, openbsd, netbsd
|
||||
package filepath
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
SEPARATOR :: '/'
|
||||
SEPARATOR_STRING :: `/`
|
||||
@@ -28,11 +24,11 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
||||
rel = "."
|
||||
}
|
||||
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
|
||||
path_ptr := realpath(rel_cstr, nil)
|
||||
path_ptr := posix.realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", __error()^ == 0
|
||||
return "", posix.errno() == nil
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
defer posix.free(path_ptr)
|
||||
|
||||
path_str := strings.clone(string(path_ptr), allocator)
|
||||
return path_str, true
|
||||
@@ -48,26 +44,3 @@ join :: proc(elems: []string, allocator := context.allocator) -> (joined: string
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
foreign libc {
|
||||
realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
|
||||
}
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
|
||||
@(private)
|
||||
foreign libc {
|
||||
@(link_name="__error") __error :: proc() -> ^i32 ---
|
||||
}
|
||||
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
@(private)
|
||||
foreign libc {
|
||||
@(link_name="__errno") __error :: proc() -> ^i32 ---
|
||||
}
|
||||
} else {
|
||||
@(private)
|
||||
foreign libc {
|
||||
@(link_name="__errno_location") __error :: proc() -> ^i32 ---
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ Type_Info_Enum :: runtime.Type_Info_Enum
|
||||
Type_Info_Map :: runtime.Type_Info_Map
|
||||
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
|
||||
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
|
||||
Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
|
||||
Type_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
|
||||
Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field
|
||||
@@ -67,8 +65,6 @@ Type_Kind :: enum {
|
||||
Map,
|
||||
Bit_Set,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
Bit_Field,
|
||||
@@ -80,35 +76,33 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
ti := type_info_of(T)
|
||||
if ti != nil {
|
||||
switch _ in ti.variant {
|
||||
case Type_Info_Named: return .Named
|
||||
case Type_Info_Integer: return .Integer
|
||||
case Type_Info_Rune: return .Rune
|
||||
case Type_Info_Float: return .Float
|
||||
case Type_Info_Complex: return .Complex
|
||||
case Type_Info_Quaternion: return .Quaternion
|
||||
case Type_Info_String: return .String
|
||||
case Type_Info_Boolean: return .Boolean
|
||||
case Type_Info_Any: return .Any
|
||||
case Type_Info_Type_Id: return .Type_Id
|
||||
case Type_Info_Pointer: return .Pointer
|
||||
case Type_Info_Multi_Pointer: return .Multi_Pointer
|
||||
case Type_Info_Procedure: return .Procedure
|
||||
case Type_Info_Array: return .Array
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array
|
||||
case Type_Info_Slice: return .Slice
|
||||
case Type_Info_Parameters: return .Tuple
|
||||
case Type_Info_Struct: return .Struct
|
||||
case Type_Info_Union: return .Union
|
||||
case Type_Info_Enum: return .Enum
|
||||
case Type_Info_Map: return .Map
|
||||
case Type_Info_Bit_Set: return .Bit_Set
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
case Type_Info_Bit_Field: return .Bit_Field
|
||||
case Type_Info_Named: return .Named
|
||||
case Type_Info_Integer: return .Integer
|
||||
case Type_Info_Rune: return .Rune
|
||||
case Type_Info_Float: return .Float
|
||||
case Type_Info_Complex: return .Complex
|
||||
case Type_Info_Quaternion: return .Quaternion
|
||||
case Type_Info_String: return .String
|
||||
case Type_Info_Boolean: return .Boolean
|
||||
case Type_Info_Any: return .Any
|
||||
case Type_Info_Type_Id: return .Type_Id
|
||||
case Type_Info_Pointer: return .Pointer
|
||||
case Type_Info_Multi_Pointer: return .Multi_Pointer
|
||||
case Type_Info_Procedure: return .Procedure
|
||||
case Type_Info_Array: return .Array
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array
|
||||
case Type_Info_Slice: return .Slice
|
||||
case Type_Info_Parameters: return .Tuple
|
||||
case Type_Info_Struct: return .Struct
|
||||
case Type_Info_Union: return .Union
|
||||
case Type_Info_Enum: return .Enum
|
||||
case Type_Info_Map: return .Map
|
||||
case Type_Info_Bit_Set: return .Bit_Set
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
case Type_Info_Bit_Field: return .Bit_Field
|
||||
}
|
||||
|
||||
}
|
||||
@@ -723,6 +717,27 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether the value given has a defined name in the enum type.
|
||||
*/
|
||||
@(require_results)
|
||||
enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
|
||||
when len(T) == cap(T) {
|
||||
return value >= min(T) && value <= max(T)
|
||||
} else {
|
||||
if value < min(T) || value > max(T) {
|
||||
return false
|
||||
}
|
||||
|
||||
for valid_value in T {
|
||||
if valid_value == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1467,21 +1482,6 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
relative_pointer_to_absolute :: proc(a: any) -> rawptr {
|
||||
if a == nil { return nil }
|
||||
a := a
|
||||
ti := runtime.type_info_core(type_info_of(a.id))
|
||||
a.id = ti.id
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Relative_Pointer:
|
||||
return relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
|
||||
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
|
||||
@@ -1543,10 +1543,6 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
case cstring: value = rawptr(v)
|
||||
case: valid = false
|
||||
}
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
valid = true
|
||||
value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -1656,8 +1652,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
Type_Info_Bit_Set,
|
||||
Type_Info_Enum,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Matrix:
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
|
||||
@@ -158,14 +158,6 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
case Type_Info_Simd_Vector:
|
||||
y := b.variant.(Type_Info_Simd_Vector) or_return
|
||||
return x.count == y.count && x.elem == y.elem
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
y := b.variant.(Type_Info_Relative_Pointer) or_return
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer
|
||||
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer
|
||||
|
||||
case Type_Info_Matrix:
|
||||
y := b.variant.(Type_Info_Matrix) or_return
|
||||
@@ -392,18 +384,6 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
|
||||
return ok
|
||||
}
|
||||
@(require_results)
|
||||
is_relative_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
|
||||
return ok
|
||||
}
|
||||
@(require_results)
|
||||
is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
@@ -736,18 +716,6 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
|
||||
io.write_i64(w, i64(info.count), 10, &n) or_return
|
||||
io.write_byte(w, ']', &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
io.write_string(w, "#relative(", &n) or_return
|
||||
write_type(w, info.base_integer, &n) or_return
|
||||
io.write_string(w, ") ", &n) or_return
|
||||
write_type(w, info.pointer, &n) or_return
|
||||
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
io.write_string(w, "#relative(", &n) or_return
|
||||
write_type(w, info.base_integer, &n) or_return
|
||||
io.write_string(w, ") ", &n) or_return
|
||||
write_type(w, info.pointer, &n) or_return
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
|
||||
@@ -240,7 +240,7 @@ _mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
|
||||
}
|
||||
@(require_results, enable_target_feature="sse2")
|
||||
_mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
|
||||
return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8)
|
||||
return transmute(__m128i)psraiw(transmute(i16x8)a, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse2")
|
||||
_mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
|
||||
@@ -262,7 +262,7 @@ _mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
|
||||
}
|
||||
@(require_results, enable_target_feature="sse2")
|
||||
_mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
|
||||
return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8)
|
||||
return transmute(__m128i)psrliw(transmute(i16x8)a, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse2")
|
||||
_mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
|
||||
|
||||
@@ -48,11 +48,11 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
|
||||
return
|
||||
}
|
||||
|
||||
map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
|
||||
map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V), err: runtime.Allocator_Error) #no_bounds_check {
|
||||
m := m
|
||||
rm := (^runtime.Raw_Map)(&m)
|
||||
|
||||
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
|
||||
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
|
||||
if info.map_info != nil {
|
||||
entries = make(type_of(entries), len(m), allocator) or_return
|
||||
|
||||
@@ -61,8 +61,8 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
|
||||
entry_index := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
|
||||
key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
entries[entry_index].hash = hash
|
||||
entries[entry_index].key = (^K)(key)^
|
||||
entries[entry_index].value = (^V)(value)^
|
||||
|
||||
@@ -36,6 +36,17 @@ to_bytes :: proc "contextless" (s: []$T) -> []byte {
|
||||
return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
|
||||
}
|
||||
|
||||
/*
|
||||
Turns a byte slice into a type.
|
||||
*/
|
||||
@(require_results)
|
||||
to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
|
||||
if len(buf) < size_of(T) {
|
||||
return {}, false
|
||||
}
|
||||
return intrinsics.unaligned_load((^T)(raw_data(buf))), true
|
||||
}
|
||||
|
||||
/*
|
||||
Turn a slice of one type, into a slice of another type.
|
||||
|
||||
@@ -96,9 +107,37 @@ contains :: proc(array: $T/[]$E, value: E) -> bool where intrinsics.type_is_comp
|
||||
return found
|
||||
}
|
||||
|
||||
/*
|
||||
Searches the given slice for the given element in O(n) time.
|
||||
|
||||
If you need a custom search condition, see `linear_search_proc`
|
||||
|
||||
Inputs:
|
||||
- array: The slice to search in.
|
||||
- key: The element to search for.
|
||||
|
||||
Returns:
|
||||
- index: The index `i`, such that `array[i]` is the first occurrence of `key` in `array`, or -1 if `key` is not present in `array`.
|
||||
|
||||
Example:
|
||||
index: int
|
||||
found: bool
|
||||
|
||||
a := []i32{10, 10, 10, 20}
|
||||
|
||||
index, found = linear_search_reverse(a, 10)
|
||||
assert(index == 0 && found == true)
|
||||
|
||||
index, found = linear_search_reverse(a, 30)
|
||||
assert(index == -1 && found == false)
|
||||
|
||||
// Note that `index == 1`, since it is relative to `a[2:]`
|
||||
index, found = linear_search_reverse(a[2:], 20)
|
||||
assert(index == 1 && found == true)
|
||||
*/
|
||||
@(require_results)
|
||||
linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||
where intrinsics.type_is_comparable(T) #no_bounds_check {
|
||||
where intrinsics.type_is_comparable(T) {
|
||||
for x, i in array {
|
||||
if x == key {
|
||||
return i, true
|
||||
@@ -107,8 +146,18 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||
return -1, false
|
||||
}
|
||||
|
||||
/*
|
||||
Searches the given slice for the first element satisfying predicate `f` in O(n) time.
|
||||
|
||||
Inputs:
|
||||
- array: The slice to search in.
|
||||
- f: The search condition.
|
||||
|
||||
Returns:
|
||||
- index: The index `i`, such that `array[i]` is the first `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist.
|
||||
*/
|
||||
@(require_results)
|
||||
linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) #no_bounds_check {
|
||||
linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) {
|
||||
for x, i in array {
|
||||
if f(x) {
|
||||
return i, true
|
||||
@@ -118,22 +167,88 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
|
||||
}
|
||||
|
||||
/*
|
||||
Binary search searches the given slice for the given element.
|
||||
If the slice is not sorted, the returned index is unspecified and meaningless.
|
||||
Searches the given slice for the given element in O(n) time, starting from the
|
||||
slice end.
|
||||
|
||||
If the value is found then the returned int is the index of the matching element.
|
||||
If there are multiple matches, then any one of the matches could be returned.
|
||||
If you need a custom search condition, see `linear_search_reverse_proc`
|
||||
|
||||
If the value is not found then the returned int is the index where a matching
|
||||
element could be inserted while maintaining sorted order.
|
||||
Inputs:
|
||||
- array: The slice to search in.
|
||||
- key: The element to search for.
|
||||
|
||||
# Examples
|
||||
Returns:
|
||||
- index: The index `i`, such that `array[i]` is the last occurrence of `key` in `array`, or -1 if `key` is not present in `array`.
|
||||
|
||||
Example:
|
||||
index: int
|
||||
found: bool
|
||||
|
||||
a := []i32{10, 10, 10, 20}
|
||||
|
||||
index, found = linear_search_reverse(a, 20)
|
||||
assert(index == 3 && found == true)
|
||||
|
||||
index, found = linear_search_reverse(a, 10)
|
||||
assert(index == 2 && found == true)
|
||||
|
||||
index, found = linear_search_reverse(a, 30)
|
||||
assert(index == -1 && found == false)
|
||||
|
||||
// Note that `index == 1`, since it is relative to `a[2:]`
|
||||
index, found = linear_search_reverse(a[2:], 20)
|
||||
assert(index == 1 && found == true)
|
||||
*/
|
||||
@(require_results)
|
||||
linear_search_reverse :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||
where intrinsics.type_is_comparable(T) {
|
||||
#reverse for x, i in array {
|
||||
if x == key {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
/*
|
||||
Searches the given slice for the last element satisfying predicate `f` in O(n)
|
||||
time, starting from the slice end.
|
||||
|
||||
Inputs:
|
||||
- array: The slice to search in.
|
||||
- f: The search condition.
|
||||
|
||||
Returns:
|
||||
- index: The index `i`, such that `array[i]` is the last `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist.
|
||||
*/
|
||||
@(require_results)
|
||||
linear_search_reverse_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) {
|
||||
#reverse for x, i in array {
|
||||
if f(x) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
/*
|
||||
Searches the given slice for the given element.
|
||||
If the slice is not sorted, the returned index is unspecified and meaningless.
|
||||
|
||||
If the value is found then the returned int is the index of the matching element.
|
||||
If there are multiple matches, then any one of the matches could be returned.
|
||||
|
||||
If the value is not found then the returned int is the index where a matching
|
||||
element could be inserted while maintaining sorted order.
|
||||
|
||||
For slices of more complex types see: `binary_search_by`
|
||||
|
||||
Example:
|
||||
/*
|
||||
Looks up a series of four elements. The first is found, with a
|
||||
uniquely determined position; the second and third are not
|
||||
found; the fourth could match any position in `[1, 4]`.
|
||||
*/
|
||||
|
||||
```
|
||||
index: int
|
||||
found: bool
|
||||
|
||||
@@ -150,9 +265,6 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
|
||||
|
||||
index, found = slice.binary_search(s, 1)
|
||||
assert(index >= 1 && index <= 4 && found == true)
|
||||
```
|
||||
|
||||
For slices of more complex types see: binary_search_by
|
||||
*/
|
||||
@(require_results)
|
||||
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||
@@ -161,21 +273,21 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||
}
|
||||
|
||||
/*
|
||||
Binary search searches the given slice for the given element.
|
||||
If the slice is not sorted, the returned index is unspecified and meaningless.
|
||||
Searches the given slice for the given element.
|
||||
If the slice is not sorted, the returned index is unspecified and meaningless.
|
||||
|
||||
If the value is found then the returned int is the index of the matching element.
|
||||
If there are multiple matches, then any one of the matches could be returned.
|
||||
If the value is found then the returned int is the index of the matching element.
|
||||
If there are multiple matches, then any one of the matches could be returned.
|
||||
|
||||
If the value is not found then the returned int is the index where a matching
|
||||
element could be inserted while maintaining sorted order.
|
||||
If the value is not found then the returned int is the index where a matching
|
||||
element could be inserted while maintaining sorted order.
|
||||
|
||||
The array elements and key may be different types. This allows the filter procedure
|
||||
to compare keys against a slice of structs, one struct value at a time.
|
||||
The array elements and key may be different types. This allows the filter procedure
|
||||
to compare keys against a slice of structs, one struct value at a time.
|
||||
|
||||
Returns:
|
||||
index: int
|
||||
found: bool
|
||||
Returns:
|
||||
- index: int
|
||||
- found: bool
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
@@ -359,6 +471,12 @@ is_empty :: proc(a: $T/[]$E) -> bool {
|
||||
return len(a) == 0
|
||||
}
|
||||
|
||||
// Gets the byte size of the backing data
|
||||
@(require_results)
|
||||
size :: proc "contextless" (a: $T/[]$E) -> int {
|
||||
return len(a) * size_of(E)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
|
||||
@@ -1121,6 +1121,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
|
||||
break trunc_block
|
||||
}
|
||||
f := f64(mantissa)
|
||||
f_abs := f
|
||||
if neg {
|
||||
f = -f
|
||||
}
|
||||
@@ -1132,7 +1133,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
|
||||
f *= pow10[exp-22]
|
||||
exp = 22
|
||||
}
|
||||
if f > 1e15 || f < 1e-15 {
|
||||
if f_abs > 1e15 || f_abs < 1e-15 {
|
||||
break trunc_block
|
||||
}
|
||||
return f * pow10[exp], nr, true
|
||||
|
||||
@@ -752,7 +752,7 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0)) -> (res: st
|
||||
count += 1
|
||||
}
|
||||
|
||||
if rune_length <= 1 {
|
||||
if rune_length < 1 {
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,16 @@ Application_setMainMenu :: proc "c" (self: ^Application, menu: ^Menu) {
|
||||
msgSend(nil, self, "setMainMenu:", menu)
|
||||
}
|
||||
|
||||
@(objc_type=Application, objc_name="mainWindow")
|
||||
Application_mainWindow :: proc "c" (self: ^Application) -> ^Window {
|
||||
return msgSend(^Window, self, "mainWindow")
|
||||
}
|
||||
|
||||
@(objc_type=Application, objc_name="keyWindow")
|
||||
Application_keyWindow :: proc "c" (self: ^Application) -> ^Window {
|
||||
return msgSend(^Window, self, "keyWindow")
|
||||
}
|
||||
|
||||
@(objc_type=Application, objc_name="windows")
|
||||
Application_windows :: proc "c" (self: ^Application) -> ^Array {
|
||||
return msgSend(^Array, self, "windows")
|
||||
|
||||
@@ -3,17 +3,18 @@ package darwin
|
||||
foreign import mach "system:System.framework"
|
||||
|
||||
import "core:c"
|
||||
import "base:intrinsics"
|
||||
|
||||
// NOTE(tetra): Unclear whether these should be aligned 16 or not.
|
||||
// However all other sync primitives are aligned for robustness.
|
||||
// I cannot currently align these though.
|
||||
// See core/sys/unix/pthread_linux.odin/pthread_t.
|
||||
mach_port_t :: distinct i32
|
||||
mach_port_t :: distinct c.uint
|
||||
task_t :: mach_port_t
|
||||
|
||||
semaphore_t :: distinct u64
|
||||
|
||||
kern_return_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
|
||||
@@ -37,11 +38,6 @@ MACH_MSGH_BITS_COMPLEX :: 0x80000000
|
||||
MACH_PORT_RIGHT_SEND :: 0
|
||||
MACH_PORT_RIGHT_RECEIVE :: 1
|
||||
|
||||
VM_PROT_NONE :: 0
|
||||
VM_PROT_READ :: 1
|
||||
VM_PROT_WRITE :: 2
|
||||
VM_PROT_EXECUTE :: 4
|
||||
|
||||
VM_INHERIT_SHARE :: 0
|
||||
VM_INHERIT_COPY :: 1
|
||||
VM_INHERIT_NONE :: 2
|
||||
@@ -58,6 +54,27 @@ 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
|
||||
|
||||
vm_size_t :: distinct c.uintptr_t
|
||||
|
||||
vm_address_t :: vm_offset_t
|
||||
vm_offset_t :: distinct c.uintptr_t
|
||||
|
||||
// NOTE(beau): typedefed to int in the original headers
|
||||
boolean_t :: b32
|
||||
|
||||
vm_prot_t :: distinct c.int
|
||||
|
||||
vm_inherit_t :: distinct c.uint
|
||||
|
||||
mach_port_name_t :: distinct c.uint
|
||||
|
||||
sync_policy_t :: distinct c.int
|
||||
|
||||
mach_msg_port_descriptor_t :: struct {
|
||||
name: mach_port_t,
|
||||
_: u32,
|
||||
@@ -257,20 +274,489 @@ foreign mach {
|
||||
task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t ---
|
||||
task_terminate :: proc(task: task_t) -> kern_return_t ---
|
||||
|
||||
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 ---
|
||||
|
||||
semaphore_signal :: proc(semaphore: semaphore_t) -> Kern_Return ---
|
||||
semaphore_signal_all :: proc(semaphore: semaphore_t) -> Kern_Return ---
|
||||
semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_t) -> Kern_Return ---
|
||||
|
||||
semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return ---
|
||||
|
||||
thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t ---
|
||||
thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t ---
|
||||
|
||||
bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t ---
|
||||
bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t ---
|
||||
|
||||
semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t ---
|
||||
semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t ---
|
||||
vm_map :: proc(
|
||||
target_task: vm_map_t,
|
||||
address: ^vm_address_t,
|
||||
size: vm_size_t,
|
||||
mask: vm_address_t,
|
||||
flags: VM_Flags,
|
||||
object: mem_entry_name_port_t,
|
||||
offset: vm_offset_t,
|
||||
copy: boolean_t,
|
||||
cur_protection,
|
||||
max_protection: VM_Prot_Flags,
|
||||
inheritance: VM_Inherit,
|
||||
) -> Kern_Return ---
|
||||
|
||||
semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t ---
|
||||
semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t ---
|
||||
semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t ---
|
||||
|
||||
semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t ---
|
||||
mach_make_memory_entry :: proc(
|
||||
target_task: vm_map_t,
|
||||
size: ^vm_size_t,
|
||||
offset: vm_offset_t,
|
||||
permission: VM_Prot_Flags,
|
||||
object_handle: ^mem_entry_name_port_t,
|
||||
parent_entry: mem_entry_name_port_t,
|
||||
) -> Kern_Return ---
|
||||
}
|
||||
|
||||
|
||||
|
||||
Kern_Return :: enum kern_return_t {
|
||||
Success,
|
||||
|
||||
/* Specified address is not currently valid.
|
||||
*/
|
||||
Invalid_Address,
|
||||
|
||||
/* Specified memory is valid, but does not permit the
|
||||
* required forms of access.
|
||||
*/
|
||||
Protection_Failure,
|
||||
|
||||
/* The address range specified is already in use, or
|
||||
* no address range of the size specified could be
|
||||
* found.
|
||||
*/
|
||||
No_Space,
|
||||
|
||||
/* The function requested was not applicable to this
|
||||
* type of argument, or an argument is invalid
|
||||
*/
|
||||
Invalid_Argument,
|
||||
|
||||
/* The function could not be performed. A catch-all.
|
||||
*/
|
||||
Failure,
|
||||
|
||||
/* A system resource could not be allocated to fulfill
|
||||
* this request. This failure may not be permanent.
|
||||
*/
|
||||
Resource_Shortage,
|
||||
|
||||
/* The task in question does not hold receive rights
|
||||
* for the port argument.
|
||||
*/
|
||||
Not_Receiver,
|
||||
|
||||
/* Bogus access restriction.
|
||||
*/
|
||||
No_Access,
|
||||
|
||||
/* During a page fault, the target address refers to a
|
||||
* memory object that has been destroyed. This
|
||||
* failure is permanent.
|
||||
*/
|
||||
Memory_Failure,
|
||||
|
||||
/* During a page fault, the memory object indicated
|
||||
* that the data could not be returned. This failure
|
||||
* may be temporary; future attempts to access this
|
||||
* same data may succeed, as defined by the memory
|
||||
* object.
|
||||
*/
|
||||
Memory_Error,
|
||||
|
||||
/* The receive right is already a member of the portset.
|
||||
*/
|
||||
Already_In_Set,
|
||||
|
||||
/* The receive right is not a member of a port set.
|
||||
*/
|
||||
Not_In_Set,
|
||||
|
||||
/* The name already denotes a right in the task.
|
||||
*/
|
||||
Name_Exists,
|
||||
|
||||
/* The operation was aborted. Ipc code will
|
||||
* catch this and reflect it as a message error.
|
||||
*/
|
||||
Aborted,
|
||||
|
||||
/* The name doesn't denote a right in the task.
|
||||
*/
|
||||
Invalid_Name,
|
||||
|
||||
/* Target task isn't an active task.
|
||||
*/
|
||||
Invalid_Task,
|
||||
|
||||
/* The name denotes a right, but not an appropriate right.
|
||||
*/
|
||||
Invalid_Right,
|
||||
|
||||
/* A blatant range error.
|
||||
*/
|
||||
Invalid_Value,
|
||||
|
||||
/* Operation would overflow limit on user-references.
|
||||
*/
|
||||
URefs_Overflow,
|
||||
|
||||
/* The supplied (port) capability is improper.
|
||||
*/
|
||||
Invalid_Capability,
|
||||
|
||||
/* The task already has send or receive rights
|
||||
* for the port under another name.
|
||||
*/
|
||||
Right_Exists,
|
||||
|
||||
/* Target host isn't actually a host.
|
||||
*/
|
||||
Invalid_Host,
|
||||
|
||||
/* An attempt was made to supply "precious" data
|
||||
* for memory that is already present in a
|
||||
* memory object.
|
||||
*/
|
||||
Memory_Present,
|
||||
|
||||
/* A page was requested of a memory manager via
|
||||
* memory_object_data_request for an object using
|
||||
* a MEMORY_OBJECT_COPY_CALL strategy, with the
|
||||
* VM_PROT_WANTS_COPY flag being used to specify
|
||||
* that the page desired is for a copy of the
|
||||
* object, and the memory manager has detected
|
||||
* the page was pushed into a copy of the object
|
||||
* while the kernel was walking the shadow chain
|
||||
* from the copy to the object. This error code
|
||||
* is delivered via memory_object_data_error
|
||||
* and is handled by the kernel (it forces the
|
||||
* kernel to restart the fault). It will not be
|
||||
* seen by users.
|
||||
*/
|
||||
Memory_Data_Moved,
|
||||
|
||||
/* A strategic copy was attempted of an object
|
||||
* upon which a quicker copy is now possible.
|
||||
* The caller should retry the copy using
|
||||
* vm_object_copy_quickly. This error code
|
||||
* is seen only by the kernel.
|
||||
*/
|
||||
Memory_Restart_Copy,
|
||||
|
||||
/* An argument applied to assert processor set privilege
|
||||
* was not a processor set control port.
|
||||
*/
|
||||
Invalid_Processor_Set,
|
||||
|
||||
/* The specified scheduling attributes exceed the thread's
|
||||
* limits.
|
||||
*/
|
||||
Policy_Limit,
|
||||
|
||||
/* The specified scheduling policy is not currently
|
||||
* enabled for the processor set.
|
||||
*/
|
||||
Invalid_Policy,
|
||||
|
||||
/* The external memory manager failed to initialize the
|
||||
* memory object.
|
||||
*/
|
||||
Invalid_Object,
|
||||
|
||||
/* A thread is attempting to wait for an event for which
|
||||
* there is already a waiting thread.
|
||||
*/
|
||||
Already_Waiting,
|
||||
|
||||
/* An attempt was made to destroy the default processor
|
||||
* set.
|
||||
*/
|
||||
Default_Set,
|
||||
|
||||
/* An attempt was made to fetch an exception port that is
|
||||
* protected, or to abort a thread while processing a
|
||||
* protected exception.
|
||||
*/
|
||||
Exception_Protected,
|
||||
|
||||
/* A ledger was required but not supplied.
|
||||
*/
|
||||
Invalid_Ledger,
|
||||
|
||||
/* The port was not a memory cache control port.
|
||||
*/
|
||||
Invalid_Memory_Control,
|
||||
|
||||
/* An argument supplied to assert security privilege
|
||||
* was not a host security port.
|
||||
*/
|
||||
Invalid_Security,
|
||||
|
||||
/* thread_depress_abort was called on a thread which
|
||||
* was not currently depressed.
|
||||
*/
|
||||
Not_Depressed,
|
||||
|
||||
/* Object has been terminated and is no longer available
|
||||
*/
|
||||
Terminated,
|
||||
|
||||
/* Lock set has been destroyed and is no longer available.
|
||||
*/
|
||||
Lock_Set_Destroyed,
|
||||
|
||||
/* The thread holding the lock terminated before releasing
|
||||
* the lock
|
||||
*/
|
||||
Lock_Unstable,
|
||||
|
||||
/* The lock is already owned by another thread
|
||||
*/
|
||||
Lock_Owned,
|
||||
|
||||
/* The lock is already owned by the calling thread
|
||||
*/
|
||||
Lock_Owned_Self,
|
||||
|
||||
/* Semaphore has been destroyed and is no longer available.
|
||||
*/
|
||||
Semaphore_Destroyed,
|
||||
|
||||
/* Return from RPC indicating the target server was
|
||||
* terminated before it successfully replied
|
||||
*/
|
||||
Rpc_Server_Terminated,
|
||||
|
||||
/* Terminate an orphaned activation.
|
||||
*/
|
||||
RPC_Terminate_Orphan,
|
||||
|
||||
/* Allow an orphaned activation to continue executing.
|
||||
*/
|
||||
RPC_Continue_Orphan,
|
||||
|
||||
/* Empty thread activation (No thread linked to it)
|
||||
*/
|
||||
Not_Supported,
|
||||
|
||||
/* Remote node down or inaccessible.
|
||||
*/
|
||||
Node_Down,
|
||||
|
||||
/* A signalled thread was not actually waiting. */
|
||||
Not_Waiting,
|
||||
|
||||
/* Some thread-oriented operation (semaphore_wait) timed out
|
||||
*/
|
||||
Operation_Timed_Out,
|
||||
|
||||
/* During a page fault, indicates that the page was rejected
|
||||
* as a result of a signature check.
|
||||
*/
|
||||
Codesign_Error,
|
||||
|
||||
/* The requested property cannot be changed at this time.
|
||||
*/
|
||||
Policy_Static,
|
||||
|
||||
/* The provided buffer is of insufficient size for the requested data.
|
||||
*/
|
||||
Insufficient_Buffer_Size,
|
||||
|
||||
/* Denied by security policy
|
||||
*/
|
||||
Denied,
|
||||
|
||||
/* The KC on which the function is operating is missing
|
||||
*/
|
||||
Missing_KC,
|
||||
|
||||
/* The KC on which the function is operating is invalid
|
||||
*/
|
||||
Invalid_KC,
|
||||
|
||||
/* A search or query operation did not return a result
|
||||
*/
|
||||
Not_Found,
|
||||
|
||||
/* Maximum return value allowable
|
||||
*/
|
||||
Return_Max = 0x100,
|
||||
}
|
||||
|
||||
/*
|
||||
* VM allocation flags:
|
||||
*
|
||||
* VM_FLAGS_FIXED
|
||||
* (really the absence of VM_FLAGS_ANYWHERE)
|
||||
* Allocate new VM region at the specified virtual address, if possible.
|
||||
*
|
||||
* VM_FLAGS_ANYWHERE
|
||||
* Allocate new VM region anywhere it would fit in the address space.
|
||||
*
|
||||
* VM_FLAGS_PURGABLE
|
||||
* Create a purgable VM object for that new VM region.
|
||||
*
|
||||
* VM_FLAGS_4GB_CHUNK
|
||||
* The new VM region will be chunked up into 4GB sized pieces.
|
||||
*
|
||||
* VM_FLAGS_NO_PMAP_CHECK
|
||||
* (for DEBUG kernel config only, ignored for other configs)
|
||||
* Do not check that there is no stale pmap mapping for the new VM region.
|
||||
* This is useful for kernel memory allocations at bootstrap when building
|
||||
* the initial kernel address space while some memory is already in use.
|
||||
*
|
||||
* VM_FLAGS_OVERWRITE
|
||||
* The new VM region can replace existing VM regions if necessary
|
||||
* (to be used in combination with VM_FLAGS_FIXED).
|
||||
*
|
||||
* VM_FLAGS_NO_CACHE
|
||||
* Pages brought in to this VM region are placed on the speculative
|
||||
* queue instead of the active queue. In other words, they are not
|
||||
* cached so that they will be stolen first if memory runs low.
|
||||
*/
|
||||
|
||||
@(private="file")
|
||||
LOG2 :: intrinsics.constant_log2
|
||||
|
||||
VM_Flag :: enum c.int {
|
||||
Anywhere,
|
||||
Purgable,
|
||||
_4GB_Chunk,
|
||||
Random_Addr,
|
||||
No_Cache,
|
||||
Resilient_Codesign,
|
||||
Resilient_Media,
|
||||
Permanent,
|
||||
|
||||
// NOTE(beau): log 2 of the bit we want in the bit set so we get that bit in
|
||||
// the bit set
|
||||
|
||||
TPRO = LOG2(0x1000),
|
||||
Overwrite = LOG2(0x4000),/* delete any existing mappings first */
|
||||
|
||||
Superpage_Size_Any = LOG2(0x10000),
|
||||
Superpage_Size_2MB = LOG2(0x20000),
|
||||
__Superpage3 = LOG2(0x40000),
|
||||
|
||||
Return_Data_Addr = LOG2(0x100000),
|
||||
Return_4K_Data_Addr = LOG2(0x800000),
|
||||
|
||||
Alias_Mask1 = 24,
|
||||
Alias_Mask2,
|
||||
Alias_Mask3,
|
||||
Alias_Mask4,
|
||||
Alias_Mask5,
|
||||
Alias_Mask6,
|
||||
Alias_Mask7,
|
||||
Alias_Mask8,
|
||||
|
||||
HW = TPRO,
|
||||
}
|
||||
|
||||
VM_Flags :: distinct bit_set[VM_Flag; c.int]
|
||||
VM_FLAGS_FIXED :: VM_Flags{}
|
||||
|
||||
/*
|
||||
* VM_FLAGS_SUPERPAGE_MASK
|
||||
* 3 bits that specify whether large pages should be used instead of
|
||||
* base pages (!=0), as well as the requested page size.
|
||||
*/
|
||||
VM_FLAGS_SUPERPAGE_MASK :: VM_Flags {
|
||||
.Superpage_Size_Any,
|
||||
.Superpage_Size_2MB,
|
||||
.__Superpage3,
|
||||
}
|
||||
|
||||
// 0xFF000000
|
||||
VM_FLAGS_ALIAS_MASK :: VM_Flags {
|
||||
.Alias_Mask1,
|
||||
.Alias_Mask2,
|
||||
.Alias_Mask3,
|
||||
.Alias_Mask4,
|
||||
.Alias_Mask5,
|
||||
.Alias_Mask6,
|
||||
.Alias_Mask7,
|
||||
.Alias_Mask8,
|
||||
}
|
||||
|
||||
VM_GET_FLAGS_ALIAS :: proc(flags: VM_Flags) -> c.int {
|
||||
return transmute(c.int)(flags & VM_FLAGS_ALIAS_MASK) >> 24
|
||||
}
|
||||
// NOTE(beau): no need for VM_SET_FLAGS_ALIAS, just mask in things from
|
||||
// VM_Flag.Alias_Mask*
|
||||
|
||||
/* These are the flags that we accept from user-space */
|
||||
VM_FLAGS_USER_ALLOCATE :: VM_Flags {
|
||||
.Anywhere,
|
||||
.Purgable,
|
||||
._4GB_Chunk,
|
||||
.Random_Addr,
|
||||
.No_Cache,
|
||||
.Permanent,
|
||||
.Overwrite,
|
||||
} | VM_FLAGS_FIXED | VM_FLAGS_SUPERPAGE_MASK | VM_FLAGS_ALIAS_MASK
|
||||
|
||||
VM_FLAGS_USER_MAP :: VM_Flags {
|
||||
.Return_4K_Data_Addr,
|
||||
.Return_Data_Addr,
|
||||
} | VM_FLAGS_USER_ALLOCATE
|
||||
|
||||
VM_FLAGS_USER_REMAP :: VM_Flags {
|
||||
.Anywhere,
|
||||
.Random_Addr,
|
||||
.Overwrite,
|
||||
.Return_Data_Addr,
|
||||
.Resilient_Codesign,
|
||||
.Resilient_Media,
|
||||
} | VM_FLAGS_FIXED
|
||||
|
||||
VM_FLAGS_SUPERPAGE_NONE :: VM_Flags{} /* no superpages, if all bits are 0 */
|
||||
|
||||
/*
|
||||
* Protection values, defined as bits within the vm_prot_t type
|
||||
*/
|
||||
|
||||
VM_Prot :: enum vm_prot_t {
|
||||
Read,
|
||||
Write,
|
||||
Execute,
|
||||
}
|
||||
|
||||
VM_Prot_Flags :: distinct bit_set[VM_Prot; vm_prot_t]
|
||||
|
||||
VM_PROT_NONE :: VM_Prot_Flags{}
|
||||
VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write}
|
||||
VM_PROT_ALL :: VM_Prot_Flags{.Read, .Write, .Execute}
|
||||
|
||||
/*
|
||||
* Enumeration of valid values for vm_inherit_t.
|
||||
*/
|
||||
|
||||
VM_Inherit :: enum vm_inherit_t {
|
||||
Share,
|
||||
Copy,
|
||||
None,
|
||||
Donate_Copy,
|
||||
|
||||
Default = Copy,
|
||||
Last_Valid = None,
|
||||
}
|
||||
|
||||
Sync_Policy :: enum sync_policy_t {
|
||||
Fifo,
|
||||
Fixed_Priority,
|
||||
Reversed,
|
||||
Order_Mask,
|
||||
|
||||
Lifo = Fifo | Reversed,
|
||||
}
|
||||
|
||||
mach_vm_trunc_page :: proc(v: u64) -> u64 {
|
||||
|
||||
@@ -28,6 +28,23 @@ CPU_Feature :: enum u64 {
|
||||
ssse3, // Supplemental streaming SIMD extension 3
|
||||
sse41, // Streaming SIMD extension 4 and 4.1
|
||||
sse42, // Streaming SIMD extension 4 and 4.2
|
||||
|
||||
avx512bf16, // Vector Neural Network Instructions supporting bfloat16
|
||||
avx512bitalg, // Bit Algorithms
|
||||
avx512bw, // Byte and Word instructions
|
||||
avx512cd, // Conflict Detection instructions
|
||||
avx512dq, // Doubleword and Quadword instructions
|
||||
avx512er, // Exponential and Reciprocal instructions
|
||||
avx512f, // Foundation
|
||||
avx512fp16, // Vector 16-bit float instructions
|
||||
avx512ifma, // Integer Fused Multiply Add
|
||||
avx512pf, // Prefetch instructions
|
||||
avx512vbmi, // Vector Byte Manipulation Instructions
|
||||
avx512vbmi2, // Vector Byte Manipulation Instructions 2
|
||||
avx512vl, // Vector Length extensions
|
||||
avx512vnni, // Vector Neural Network Instructions
|
||||
avx512vp2intersect, // Vector Pair Intersection to a Pair of Mask Registers
|
||||
avx512vpopcntdq, // Vector Population Count for Doubleword and Quadword
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
@@ -82,9 +99,11 @@ init_cpu_features :: proc "c" () {
|
||||
//
|
||||
// See: crbug.com/375968
|
||||
os_supports_avx := false
|
||||
os_supports_avx512 := false
|
||||
if .os_xsave in set && is_set(26, ecx1) {
|
||||
eax, _ := xgetbv(0)
|
||||
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
||||
os_supports_avx512 = is_set(5, eax) && is_set(6, eax) && is_set(7, eax)
|
||||
}
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx, 28, ecx1)
|
||||
@@ -94,11 +113,37 @@ init_cpu_features :: proc "c" () {
|
||||
return
|
||||
}
|
||||
|
||||
_, ebx7, _, _ := cpuid(7, 0)
|
||||
_, ebx7, ecx7, edx7 := cpuid(7, 0)
|
||||
try_set(&set, .bmi1, 3, ebx7)
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx2, 5, ebx7)
|
||||
}
|
||||
if os_supports_avx512 {
|
||||
try_set(&set, .avx512f, 16, ebx7)
|
||||
try_set(&set, .avx512dq, 17, ebx7)
|
||||
try_set(&set, .avx512ifma, 21, ebx7)
|
||||
try_set(&set, .avx512pf, 26, ebx7)
|
||||
try_set(&set, .avx512er, 27, ebx7)
|
||||
try_set(&set, .avx512cd, 28, ebx7)
|
||||
try_set(&set, .avx512bw, 30, ebx7)
|
||||
|
||||
// XMM/YMM are also required for 128/256-bit instructions
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx512vl, 31, ebx7)
|
||||
}
|
||||
|
||||
try_set(&set, .avx512vbmi, 1, ecx7)
|
||||
try_set(&set, .avx512vbmi2, 6, ecx7)
|
||||
try_set(&set, .avx512vnni, 11, ecx7)
|
||||
try_set(&set, .avx512bitalg, 12, ecx7)
|
||||
try_set(&set, .avx512vpopcntdq, 14, ecx7)
|
||||
|
||||
try_set(&set, .avx512vp2intersect, 8, edx7)
|
||||
try_set(&set, .avx512fp16, 23, edx7)
|
||||
|
||||
eax7_1, _, _, _ := cpuid(7, 1)
|
||||
try_set(&set, .avx512bf16, 5, eax7_1)
|
||||
}
|
||||
try_set(&set, .bmi2, 8, ebx7)
|
||||
try_set(&set, .erms, 9, ebx7)
|
||||
try_set(&set, .rdseed, 18, ebx7)
|
||||
|
||||
@@ -494,6 +494,10 @@ macos_release_map: map[string]Darwin_To_Release = {
|
||||
"21G816" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
|
||||
"21G920" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
|
||||
"21G1974" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
|
||||
"21H1015" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 3}}},
|
||||
"21H1123" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 4}}},
|
||||
"21H1222" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 5}}},
|
||||
"21H1320" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 6}}},
|
||||
|
||||
// MacOS Ventura
|
||||
"22A380" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 0}}},
|
||||
@@ -513,6 +517,15 @@ macos_release_map: map[string]Darwin_To_Release = {
|
||||
"22G120" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 0}}},
|
||||
"22G313" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 1}}},
|
||||
"22G320" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 2}}},
|
||||
"22G436" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 3}}},
|
||||
"22G513" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 4}}},
|
||||
"22G621" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 5}}},
|
||||
"22G630" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 6}}},
|
||||
"22G720" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 7}}},
|
||||
"22G820" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 8}}},
|
||||
"22G830" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 9}}},
|
||||
"22H123" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 0}}},
|
||||
"22H221" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 1}}},
|
||||
|
||||
// MacOS Sonoma
|
||||
"23A344" = {{23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
|
||||
@@ -531,9 +544,12 @@ macos_release_map: map[string]Darwin_To_Release = {
|
||||
"23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
|
||||
"23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
|
||||
"23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
|
||||
"23H222" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 1}}},
|
||||
|
||||
// MacOS Sequoia
|
||||
"24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
|
||||
"24A348" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
|
||||
"24B83" = {{24, 1, 0}, "macOS", {"Sequoia", {15, 1, 0}}},
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -152,66 +152,43 @@ Errno :: enum i32 {
|
||||
RDONLY flag is not present, because it has the value of 0, i.e. it is the
|
||||
default, unless WRONLY or RDWR is specified.
|
||||
*/
|
||||
when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECT = 14,
|
||||
LARGEFILE = 15,
|
||||
DIRECTORY = 16,
|
||||
NOFOLLOW = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||
|
||||
} else {
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECTORY = 14,
|
||||
NOFOLLOW = 15,
|
||||
DIRECT = 16,
|
||||
LARGEFILE = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECT = 14,
|
||||
LARGEFILE = 15,
|
||||
DIRECTORY = 16,
|
||||
NOFOLLOW = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||
|
||||
/*
|
||||
Bits for FD_Flags bitset
|
||||
@@ -542,6 +519,36 @@ Fd_Poll_Events_Bits :: enum {
|
||||
RDHUP = 13,
|
||||
}
|
||||
|
||||
Inotify_Init_Bits :: enum {
|
||||
NONBLOCK = 11,
|
||||
CLOEXEC = 19,
|
||||
}
|
||||
|
||||
Inotify_Event_Bits :: enum u32 {
|
||||
ACCESS = 0,
|
||||
MODIFY = 1,
|
||||
ATTRIB = 2,
|
||||
CLOSE_WRITE = 3,
|
||||
CLOSE_NOWRITE = 4,
|
||||
OPEN = 5,
|
||||
MOVED_FROM = 6,
|
||||
MOVED_TO = 7,
|
||||
CREATE = 8,
|
||||
DELETE = 9,
|
||||
DELETE_SELF = 10,
|
||||
MOVE_SELF = 11,
|
||||
UNMOUNT = 13,
|
||||
Q_OVERFLOW = 14,
|
||||
IGNORED = 15,
|
||||
ONLYDIR = 24,
|
||||
DONT_FOLLOW = 25,
|
||||
EXCL_UNLINK = 26,
|
||||
MASK_CREATE = 28,
|
||||
MASK_ADD = 29,
|
||||
ISDIR = 30,
|
||||
ONESHOT = 31,
|
||||
}
|
||||
|
||||
/*
|
||||
Bits for Mem_Protection bitfield
|
||||
*/
|
||||
|
||||
@@ -135,6 +135,31 @@ STATX_BASIC_STATS :: Statx_Mask {
|
||||
.BLOCKS,
|
||||
}
|
||||
|
||||
IN_ALL_EVENTS :: Inotify_Event_Mask {
|
||||
.ACCESS,
|
||||
.MODIFY,
|
||||
.ATTRIB,
|
||||
.CLOSE_WRITE,
|
||||
.CLOSE_NOWRITE,
|
||||
.OPEN,
|
||||
.MOVED_FROM,
|
||||
.MOVED_TO,
|
||||
.CREATE,
|
||||
.DELETE,
|
||||
.DELETE_SELF,
|
||||
.MOVE_SELF,
|
||||
}
|
||||
|
||||
IN_CLOSE :: Inotify_Event_Mask {
|
||||
.CLOSE_WRITE,
|
||||
.CLOSE_NOWRITE,
|
||||
}
|
||||
|
||||
IN_MOVE :: Inotify_Event_Mask {
|
||||
.MOVED_FROM,
|
||||
.MOVED_TO,
|
||||
}
|
||||
|
||||
/*
|
||||
Tell `shmget` to create a new key
|
||||
*/
|
||||
|
||||
@@ -2536,11 +2536,30 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt
|
||||
|
||||
// TODO(flysand): ioprio_get
|
||||
|
||||
// TODO(flysand): inotify_init
|
||||
inotify_init :: proc "contextless" () -> (Fd, Errno) {
|
||||
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
|
||||
ret := syscall(SYS_inotify_init1, 0)
|
||||
return errno_unwrap(ret, Fd)
|
||||
} else {
|
||||
ret := syscall(SYS_inotify_init)
|
||||
return errno_unwrap(ret, Fd)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(flysand): inotify_add_watch
|
||||
inotify_init1 :: proc "contextless" (flags: Inotify_Init_Flags) -> (Fd, Errno) {
|
||||
ret := syscall(SYS_inotify_init1, transmute(i32)flags)
|
||||
return errno_unwrap(ret, Fd)
|
||||
}
|
||||
|
||||
// TODO(flysand): inotify_rm_watch
|
||||
inotify_add_watch :: proc "contextless" (fd: Fd, pathname: cstring, mask: Inotify_Event_Mask) -> (Wd, Errno) {
|
||||
ret := syscall(SYS_inotify_add_watch, fd, transmute(uintptr) pathname, transmute(u32) mask)
|
||||
return errno_unwrap(ret, Wd)
|
||||
}
|
||||
|
||||
inotify_rm_watch :: proc "contextless" (fd: Fd, wd: Wd) -> (Errno) {
|
||||
ret := syscall(SYS_inotify_rm_watch, fd, wd)
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
// TODO(flysand): migrate_pages
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ Id :: distinct uint
|
||||
*/
|
||||
Fd :: distinct i32
|
||||
|
||||
/*
|
||||
Represents a watch descriptor.
|
||||
*/
|
||||
Wd :: distinct i32
|
||||
|
||||
/*
|
||||
Type for PID file descriptors.
|
||||
*/
|
||||
@@ -343,6 +348,18 @@ Poll_Fd :: struct {
|
||||
revents: Fd_Poll_Events,
|
||||
}
|
||||
|
||||
Inotify_Init_Flags :: bit_set[Inotify_Init_Bits; i32]
|
||||
|
||||
Inotify_Event :: struct {
|
||||
wd: Wd,
|
||||
mask: Inotify_Event_Mask,
|
||||
cookie: u32,
|
||||
len: u32,
|
||||
name: [0]u8,
|
||||
}
|
||||
|
||||
Inotify_Event_Mask :: bit_set[Inotify_Event_Bits; u32]
|
||||
|
||||
/*
|
||||
Specifies protection for memory pages.
|
||||
*/
|
||||
@@ -1136,6 +1153,12 @@ when ODIN_ARCH == .arm32 {
|
||||
eflags: uint,
|
||||
rsp: uint,
|
||||
ss: uint,
|
||||
fs_base: uint,
|
||||
gs_base: uint,
|
||||
ds: uint,
|
||||
es: uint,
|
||||
fs: uint,
|
||||
gs: uint,
|
||||
}
|
||||
// All floating point state
|
||||
_Arch_User_FP_Regs :: struct {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -29,12 +30,12 @@ foreign lib {
|
||||
panic(string(posix.strerror(posix.errno())))
|
||||
}
|
||||
defer posix.free(list)
|
||||
|
||||
|
||||
entries := list[:ret]
|
||||
for entry in entries {
|
||||
log.info(entry)
|
||||
posix.free(entry)
|
||||
}
|
||||
for entry in entries {
|
||||
log.info(entry)
|
||||
posix.free(entry)
|
||||
}
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
|
||||
*/
|
||||
@@ -53,15 +54,6 @@ foreign lib {
|
||||
*/
|
||||
closedir :: proc(dirp: DIR) -> result ---
|
||||
|
||||
/*
|
||||
Return a file descriptor referring to the same directory as the dirp argument.
|
||||
|
||||
// TODO: this is a macro on NetBSD?
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
|
||||
*/
|
||||
dirfd :: proc(dirp: DIR) -> FD ---
|
||||
|
||||
/*
|
||||
Equivalent to the opendir() function except that the directory is specified by a file descriptor
|
||||
rather than by a name.
|
||||
@@ -89,16 +81,16 @@ foreign lib {
|
||||
Returns nil when the end is reached or an error occurred (which sets errno).
|
||||
|
||||
Example:
|
||||
posix.set_errno(.NONE)
|
||||
entry := posix.readdir(dirp)
|
||||
if entry == nil {
|
||||
if errno := posix.errno(); errno != .NONE {
|
||||
panic(string(posix.strerror(errno)))
|
||||
} else {
|
||||
fmt.println("end of directory stream")
|
||||
}
|
||||
} else {
|
||||
fmt.println(entry)
|
||||
posix.set_errno(.NONE)
|
||||
entry := posix.readdir(dirp)
|
||||
if entry == nil {
|
||||
if errno := posix.errno(); errno != .NONE {
|
||||
panic(string(posix.strerror(errno)))
|
||||
} else {
|
||||
fmt.println("end of directory stream")
|
||||
}
|
||||
} else {
|
||||
fmt.println(entry)
|
||||
}
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]]
|
||||
@@ -160,11 +152,36 @@ when ODIN_OS == .NetBSD {
|
||||
@(private) LSCANDIR :: "__scandir30"
|
||||
@(private) LOPENDIR :: "__opendir30"
|
||||
@(private) LREADDIR :: "__readdir30"
|
||||
|
||||
/*
|
||||
Return a file descriptor referring to the same directory as the dirp argument.
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
|
||||
*/
|
||||
dirfd :: proc "c" (dirp: DIR) -> FD {
|
||||
_dirdesc :: struct {
|
||||
dd_fd: FD,
|
||||
|
||||
// more stuff...
|
||||
}
|
||||
|
||||
return (^_dirdesc)(dirp).dd_fd
|
||||
}
|
||||
|
||||
} else {
|
||||
@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
|
||||
@(private) LSCANDIR :: "scandir" + INODE_SUFFIX
|
||||
@(private) LOPENDIR :: "opendir" + INODE_SUFFIX
|
||||
@(private) LREADDIR :: "readdir" + INODE_SUFFIX
|
||||
|
||||
foreign lib {
|
||||
/*
|
||||
Return a file descriptor referring to the same directory as the dirp argument.
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
|
||||
*/
|
||||
dirfd :: proc(dirp: DIR) -> FD ---
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
@@ -193,13 +210,21 @@ when ODIN_OS == .Darwin {
|
||||
} else when ODIN_OS == .NetBSD {
|
||||
|
||||
dirent :: struct {
|
||||
d_ino: ino_t, /* [PSX] file number of entry */
|
||||
d_reclen: c.uint16_t, /* length of this record */
|
||||
d_namelen: c.uint16_t, /* length of string in d_name */
|
||||
d_type: D_Type, /* file type */
|
||||
d_name: [512]c.char `fmt:"s,0"`, /* [PSX] entry name */
|
||||
d_ino: ino_t, /* [PSX] file number of entry */
|
||||
d_reclen: c.uint16_t, /* length of this record */
|
||||
d_namelen: c.uint16_t, /* length of string in d_name */
|
||||
d_type: D_Type, /* file type */
|
||||
d_name: [512]c.char `fmt:"s,0"`, /* [PSX] entry name */
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
dirent :: struct {
|
||||
d_ino: u64, /* [PSX] file number of entry */
|
||||
d_off: i64, /* directory offset of the next entry */
|
||||
d_reclen: u16, /* length of this record */
|
||||
d_type: D_Type, /* file type */
|
||||
d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -111,7 +112,14 @@ when ODIN_OS == .Darwin {
|
||||
|
||||
RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
RTLD_LAZY :: 0x001
|
||||
RTLD_NOW :: 0x002
|
||||
RTLD_GLOBAL :: 0x100
|
||||
|
||||
_RTLD_LOCAL :: 0
|
||||
RTLD_LOCAL :: RTLD_Flags{}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build windows, darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -141,7 +142,7 @@ when ODIN_OS == .Darwin {
|
||||
EMLINK :: 31
|
||||
EPIPE :: 32
|
||||
EAGAIN :: 35
|
||||
EWOULDBLOCK :: 35
|
||||
EWOULDBLOCK :: EAGAIN
|
||||
EINPROGRESS :: 36
|
||||
EALREADY :: 37
|
||||
ENOTSOCK :: 38
|
||||
@@ -151,7 +152,7 @@ when ODIN_OS == .Darwin {
|
||||
ENOPROTOOPT :: 42
|
||||
EPROTONOSUPPORT :: 43
|
||||
ENOTSUP :: 45
|
||||
EOPNOTSUPP :: 45
|
||||
EOPNOTSUPP :: ENOTSUP
|
||||
EAFNOSUPPORT :: 47
|
||||
EADDRINUSE :: 48
|
||||
EADDRNOTAVAIL :: 49
|
||||
@@ -220,7 +221,7 @@ when ODIN_OS == .Darwin {
|
||||
EMLINK :: 31
|
||||
EPIPE :: 32
|
||||
EAGAIN :: 35
|
||||
EWOULDBLOCK :: 35
|
||||
EWOULDBLOCK :: EAGAIN
|
||||
EINPROGRESS :: 36
|
||||
EALREADY :: 37
|
||||
ENOTSOCK :: 38
|
||||
@@ -230,7 +231,7 @@ when ODIN_OS == .Darwin {
|
||||
ENOPROTOOPT :: 42
|
||||
EPROTONOSUPPORT :: 43
|
||||
ENOTSUP :: 45
|
||||
EOPNOTSUPP :: 45
|
||||
EOPNOTSUPP :: ENOTSUP
|
||||
EAFNOSUPPORT :: 47
|
||||
EADDRINUSE :: 48
|
||||
EADDRNOTAVAIL :: 49
|
||||
@@ -301,7 +302,7 @@ when ODIN_OS == .Darwin {
|
||||
EMLINK :: 31
|
||||
EPIPE :: 32
|
||||
EAGAIN :: 35
|
||||
EWOULDBLOCK :: 35
|
||||
EWOULDBLOCK :: EAGAIN
|
||||
EINPROGRESS :: 36
|
||||
EALREADY :: 37
|
||||
ENOTSOCK :: 38
|
||||
@@ -311,7 +312,7 @@ when ODIN_OS == .Darwin {
|
||||
ENOPROTOOPT :: 42
|
||||
EPROTONOSUPPORT :: 43
|
||||
ENOTSUP :: 45
|
||||
EOPNOTSUPP :: 45
|
||||
EOPNOTSUPP :: ENOTSUP
|
||||
EAFNOSUPPORT :: 47
|
||||
EADDRINUSE :: 48
|
||||
EADDRNOTAVAIL :: 49
|
||||
@@ -367,7 +368,173 @@ when ODIN_OS == .Darwin {
|
||||
ETIME :: -1
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Linux {
|
||||
EPERM :: 1
|
||||
ENOENT :: 2
|
||||
ESRCH :: 3
|
||||
EINTR :: 4
|
||||
EIO :: 5
|
||||
ENXIO :: 6
|
||||
E2BIG :: 7
|
||||
ENOEXEC :: 8
|
||||
EBADF :: 9
|
||||
ECHILD :: 10
|
||||
EAGAIN :: 11
|
||||
EWOULDBLOCK :: EAGAIN
|
||||
ENOMEM :: 12
|
||||
EACCES :: 13
|
||||
EFAULT :: 14
|
||||
EBUSY :: 16
|
||||
EEXIST :: 17
|
||||
EXDEV :: 18
|
||||
ENODEV :: 19
|
||||
ENOTDIR :: 20
|
||||
EISDIR :: 21
|
||||
EINVAL :: 22
|
||||
ENFILE :: 23
|
||||
EMFILE :: 24
|
||||
ENOTTY :: 25
|
||||
ETXTBSY :: 26
|
||||
EFBIG :: 27
|
||||
ENOSPC :: 28
|
||||
ESPIPE :: 29
|
||||
EROFS :: 30
|
||||
EMLINK :: 31
|
||||
EPIPE :: 32
|
||||
|
||||
EDEADLK :: 35
|
||||
ENAMETOOLONG :: 36
|
||||
ENOLCK :: 37
|
||||
ENOSYS :: 38
|
||||
ENOTEMPTY :: 39
|
||||
ELOOP :: 40
|
||||
ENOMSG :: 42
|
||||
EIDRM :: 43
|
||||
|
||||
ENOSTR :: 60
|
||||
ENODATA :: 61
|
||||
ETIME :: 62
|
||||
ENOSR :: 63
|
||||
|
||||
ENOLINK :: 67
|
||||
|
||||
EPROTO :: 71
|
||||
EMULTIHOP :: 72
|
||||
EBADMSG :: 74
|
||||
EOVERFLOW :: 75
|
||||
|
||||
ENOTSOCK :: 88
|
||||
EDESTADDRREQ :: 89
|
||||
EMSGSIZE :: 90
|
||||
EPROTOTYPE :: 91
|
||||
ENOPROTOOPT :: 92
|
||||
EPROTONOSUPPORT :: 93
|
||||
|
||||
EOPNOTSUPP :: 95
|
||||
ENOTSUP :: EOPNOTSUPP
|
||||
EAFNOSUPPORT :: 97
|
||||
EADDRINUSE :: 98
|
||||
EADDRNOTAVAIL :: 99
|
||||
ENETDOWN :: 100
|
||||
ENETUNREACH :: 101
|
||||
ENETRESET :: 102
|
||||
ECONNABORTED :: 103
|
||||
ECONNRESET :: 104
|
||||
ENOBUFS :: 105
|
||||
EISCONN :: 106
|
||||
ENOTCONN :: 107
|
||||
|
||||
ETIMEDOUT :: 110
|
||||
ECONNREFUSED :: 111
|
||||
|
||||
EHOSTUNREACH :: 113
|
||||
EALREADY :: 114
|
||||
EINPROGRESS :: 115
|
||||
ESTALE :: 116
|
||||
|
||||
EDQUOT :: 122
|
||||
ECANCELED :: 125
|
||||
|
||||
EOWNERDEAD :: 130
|
||||
ENOTRECOVERABLE :: 131
|
||||
} else when ODIN_OS == .Windows {
|
||||
E2BIG :: 7
|
||||
EACCES :: 13
|
||||
EADDRINUSE :: 100
|
||||
EADDRNOTAVAIL :: 101
|
||||
EAFNOSUPPORT :: 102
|
||||
EAGAIN :: 11
|
||||
EALREADY :: 103
|
||||
EBADF :: 9
|
||||
EBADMSG :: 104
|
||||
EBUSY :: 16
|
||||
ECANCELED :: 105
|
||||
ECHILD :: 10
|
||||
ECONNABORTED :: 106
|
||||
ECONNREFUSED :: 107
|
||||
ECONNRESET :: 108
|
||||
EDEADLK :: 36
|
||||
EDESTADDRREQ :: 109
|
||||
EDQUOT :: -1 // NOTE: not defined
|
||||
EEXIST :: 17
|
||||
EFAULT :: 14
|
||||
EFBIG :: 27
|
||||
EHOSTUNREACH :: 110
|
||||
EIDRM :: 111
|
||||
EINPROGRESS :: 112
|
||||
EINTR :: 4
|
||||
EINVAL :: 22
|
||||
EIO :: 5
|
||||
EISCONN :: 113
|
||||
EISDIR :: 21
|
||||
ELOOP :: 114
|
||||
EMFILE :: 24
|
||||
EMLINK :: 31
|
||||
EMSGSIZE :: 115
|
||||
EMULTIHOP :: -1 // NOTE: not defined
|
||||
ENAMETOOLONG :: 38
|
||||
ENETDOWN :: 116
|
||||
ENETRESET :: 117
|
||||
ENETUNREACH :: 118
|
||||
ENFILE :: 23
|
||||
ENOBUFS :: 119
|
||||
ENODATA :: 120
|
||||
ENODEV :: 19
|
||||
ENOENT :: 2
|
||||
ENOEXEC :: 8
|
||||
ENOLCK :: 39
|
||||
ENOLINK :: 121
|
||||
ENOMEM :: 12
|
||||
ENOMSG :: 122
|
||||
ENOPROTOOPT :: 123
|
||||
ENOSPC :: 28
|
||||
ENOSR :: 124
|
||||
ENOSTR :: 125
|
||||
ENOSYS :: 40
|
||||
ENOTCONN :: 126
|
||||
ENOTDIR :: 20
|
||||
ENOTEMPTY :: 41
|
||||
ENOTRECOVERABLE :: 127
|
||||
ENOTSOCK :: 128
|
||||
ENOTSUP :: 129
|
||||
ENOTTY :: 25
|
||||
ENXIO :: 6
|
||||
EOPNOTSUPP :: 130
|
||||
EOVERFLOW :: 132
|
||||
EOWNERDEAD :: 133
|
||||
EPERM :: 1
|
||||
EPIPE :: 32
|
||||
EPROTO :: 134
|
||||
EPROTONOSUPPORT :: 135
|
||||
EPROTOTYPE :: 136
|
||||
EROFS :: 30
|
||||
ESPIPE :: 29
|
||||
ESRCH :: 3
|
||||
ESTALE :: -1 // NOTE: not defined
|
||||
ETIME :: 137
|
||||
ETIMEDOUT :: 138
|
||||
ETXTBSY :: 139
|
||||
EWOULDBLOCK :: 140
|
||||
EXDEV :: 18
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, openbsd, freebsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -92,9 +93,6 @@ Lock_Type :: enum c.short {
|
||||
WRLCK = F_WRLCK,
|
||||
}
|
||||
|
||||
// Assertions made to unify this bit set.
|
||||
#assert(O_RDONLY == 0)
|
||||
|
||||
O_Flag_Bits :: enum c.int {
|
||||
// Sets FD_CLOEXEC on the file descriptor.
|
||||
CLOEXEC = log2(O_CLOEXEC),
|
||||
@@ -107,11 +105,11 @@ O_Flag_Bits :: enum c.int {
|
||||
// If terminal device, do not make it the controlling terminal for the process.
|
||||
NOCTTY = log2(O_NOCTTY),
|
||||
// Don't follow symbolic links, fail with errno ELOOP.
|
||||
NOFOLLOW = log2(O_NOFOLOW),
|
||||
NOFOLLOW = log2(O_NOFOLLOW),
|
||||
// If exists and regular, truncate the length to 0.
|
||||
TRUNC = log2(O_TRUNC),
|
||||
|
||||
// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
|
||||
// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
|
||||
// this bit set enum because it is 0 on some platforms and a value on others.
|
||||
// TTY_INIT = O_TTY_INIT,
|
||||
|
||||
@@ -123,7 +121,8 @@ O_Flag_Bits :: enum c.int {
|
||||
NONBLOCK = log2(O_NONBLOCK),
|
||||
// Write I/O shall complete as defined by synchronized I/O file integrity completion.
|
||||
SYNC = log2(O_SYNC),
|
||||
// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
|
||||
|
||||
// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
|
||||
// this bit set enum because it is 0 on some platforms and a value on others.
|
||||
// RSYNC = O_RSYNC,
|
||||
|
||||
@@ -135,11 +134,10 @@ O_Flag_Bits :: enum c.int {
|
||||
WRONLY = log2(O_WRONLY),
|
||||
// Reading only.
|
||||
// RDONLY = 0, // Default
|
||||
|
||||
}
|
||||
|
||||
O_Flags :: bit_set[O_Flag_Bits; c.int]
|
||||
|
||||
// A mask of all the access mode bits.
|
||||
O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY }
|
||||
|
||||
AT_Flag_Bits :: enum c.int {
|
||||
@@ -152,8 +150,8 @@ AT_Flags :: bit_set[AT_Flag_Bits; c.int]
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
|
||||
F_DUPFD :: 0
|
||||
F_DUPFD_CLOEXEC :: 67
|
||||
@@ -178,7 +176,7 @@ when ODIN_OS == .Darwin {
|
||||
O_DIRECTORY :: 0x00100000
|
||||
O_EXCL :: 0x00000800
|
||||
O_NOCTTY :: 0x00020000
|
||||
O_NOFOLOW :: 0x00000100
|
||||
O_NOFOLLOW :: 0x00000100
|
||||
O_TRUNC :: 0x00000400
|
||||
|
||||
_O_TTY_INIT :: 0
|
||||
@@ -189,16 +187,16 @@ when ODIN_OS == .Darwin {
|
||||
O_NONBLOCK :: 0x00000004
|
||||
O_SYNC :: 0x0080
|
||||
|
||||
_O_RSYNC :: 0
|
||||
O_RSYNC :: O_Flags{}
|
||||
_O_RSYNC :: 0
|
||||
O_RSYNC :: O_Flags{}
|
||||
|
||||
O_EXEC :: 0x40000000
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
O_EXEC :: 0x40000000
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
|
||||
_O_SEARCH :: O_EXEC | O_DIRECTORY
|
||||
O_SEARCH :: O_Flags{ .EXEC, .DIRECTORY }
|
||||
O_SEARCH :: O_Flags{.EXEC, .DIRECTORY}
|
||||
|
||||
AT_FDCWD: FD: -2
|
||||
|
||||
@@ -217,8 +215,8 @@ when ODIN_OS == .Darwin {
|
||||
|
||||
} else when ODIN_OS == .FreeBSD {
|
||||
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
|
||||
F_DUPFD :: 0
|
||||
F_DUPFD_CLOEXEC :: 17
|
||||
@@ -243,7 +241,7 @@ when ODIN_OS == .Darwin {
|
||||
O_DIRECTORY :: 0x00020000
|
||||
O_EXCL :: 0x0800
|
||||
O_NOCTTY :: 0x8000
|
||||
O_NOFOLOW :: 0x0100
|
||||
O_NOFOLLOW :: 0x0100
|
||||
O_TRUNC :: 0x0400
|
||||
|
||||
_O_TTY_INIT :: 0x00080000
|
||||
@@ -256,10 +254,10 @@ when ODIN_OS == .Darwin {
|
||||
_O_RSYNC :: 0
|
||||
O_RSYNC :: O_Flags{} // NOTE: not defined in headers
|
||||
|
||||
O_EXEC :: 0x00040000
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
O_EXEC :: 0x00040000
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
|
||||
_O_SEARCH :: O_EXEC
|
||||
O_SEARCH :: O_Flags{ .EXEC }
|
||||
@@ -282,8 +280,8 @@ when ODIN_OS == .Darwin {
|
||||
|
||||
} else when ODIN_OS == .NetBSD {
|
||||
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
|
||||
F_DUPFD :: 0
|
||||
F_DUPFD_CLOEXEC :: 12
|
||||
@@ -308,7 +306,7 @@ when ODIN_OS == .Darwin {
|
||||
O_DIRECTORY :: 0x0020000
|
||||
O_EXCL :: 0x0800
|
||||
O_NOCTTY :: 0x8000
|
||||
O_NOFOLOW :: 0x0100
|
||||
O_NOFOLLOW :: 0x0100
|
||||
O_TRUNC :: 0x0400
|
||||
|
||||
_O_TTY_INIT :: 0
|
||||
@@ -319,14 +317,14 @@ when ODIN_OS == .Darwin {
|
||||
O_NONBLOCK :: 0x0004
|
||||
O_SYNC :: 0x0080
|
||||
|
||||
_O_RSYNC :: 0x0002
|
||||
O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
|
||||
_O_RSYNC :: 0x0002
|
||||
O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
|
||||
|
||||
|
||||
O_EXEC :: 0x04000000
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
O_EXEC :: 0x04000000
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
|
||||
_O_SEARCH :: 0x00800000
|
||||
O_SEARCH :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
|
||||
@@ -347,8 +345,8 @@ when ODIN_OS == .Darwin {
|
||||
}
|
||||
} else when ODIN_OS == .OpenBSD {
|
||||
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int32_t
|
||||
|
||||
F_DUPFD :: 0
|
||||
F_DUPFD_CLOEXEC :: 10
|
||||
@@ -373,7 +371,7 @@ when ODIN_OS == .Darwin {
|
||||
O_DIRECTORY :: 0x20000
|
||||
O_EXCL :: 0x0800
|
||||
O_NOCTTY :: 0x8000
|
||||
O_NOFOLOW :: 0x0100
|
||||
O_NOFOLLOW :: 0x0100
|
||||
O_TRUNC :: 0x0400
|
||||
|
||||
_O_TTY_INIT :: 0
|
||||
@@ -384,13 +382,13 @@ when ODIN_OS == .Darwin {
|
||||
O_NONBLOCK :: 0x0004
|
||||
O_SYNC :: 0x0080
|
||||
|
||||
_O_RSYNC :: O_SYNC
|
||||
O_RSYNC :: O_Flags{ .SYNC }
|
||||
_O_RSYNC :: O_SYNC
|
||||
O_RSYNC :: O_Flags{.SYNC}
|
||||
|
||||
O_EXEC :: 0x04000000 // NOTE: not defined in the headers
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
O_EXEC :: 0x04000000 // NOTE: not defined in the headers
|
||||
O_RDONLY :: 0
|
||||
O_RDWR :: 0x0002
|
||||
O_WRONLY :: 0x0001
|
||||
|
||||
_O_SEARCH :: 0
|
||||
O_SEARCH :: O_Flags{} // NOTE: not defined in the headers
|
||||
@@ -410,6 +408,70 @@ when ODIN_OS == .Darwin {
|
||||
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
off_t :: distinct c.int64_t
|
||||
pid_t :: distinct c.int
|
||||
|
||||
F_DUPFD :: 0
|
||||
F_GETFD :: 1
|
||||
F_SETFD :: 2
|
||||
F_GETFL :: 3
|
||||
F_SETFL :: 4
|
||||
F_GETLK :: 5
|
||||
F_SETLK :: 6
|
||||
F_SETLKW :: 7
|
||||
F_SETOWN :: 8
|
||||
F_GETOWN :: 9
|
||||
F_RDLCK :: 0
|
||||
F_UNLCK :: 2
|
||||
F_WRLCK :: 1
|
||||
|
||||
F_DUPFD_CLOEXEC :: 1030
|
||||
|
||||
FD_CLOEXEC :: 1
|
||||
|
||||
O_CREAT :: 0o0_000_100
|
||||
O_EXCL :: 0o0_000_200
|
||||
O_NOCTTY :: 0o0_000_400
|
||||
O_TRUNC :: 0o0_001_000
|
||||
O_DIRECTORY :: 0o0_200_000
|
||||
O_NOFOLLOW :: 0o0_400_000
|
||||
O_CLOEXEC :: 0o2_000_000
|
||||
|
||||
_O_TTY_INIT :: 0
|
||||
O_TTY_INIT :: O_Flags{}
|
||||
|
||||
O_APPEND :: 0o0_002_000
|
||||
O_NONBLOCK :: 0o0_004_000
|
||||
O_DSYNC :: 0o0_010_000
|
||||
O_SYNC :: 0o4_010_000
|
||||
|
||||
_O_RSYNC :: 0
|
||||
O_RSYNC :: O_Flags{}
|
||||
|
||||
O_EXEC :: 0x04000000 // NOTE: not defined in the headers
|
||||
|
||||
O_RDONLY :: 0
|
||||
O_WRONLY :: 0o1
|
||||
O_RDWR :: 0o2
|
||||
|
||||
_O_SEARCH :: 0
|
||||
O_SEARCH :: O_Flags{}
|
||||
|
||||
AT_FDCWD: FD: -100
|
||||
|
||||
AT_EACCESS :: 0x200
|
||||
AT_SYMLINK_NOFOLLOW :: 0x100
|
||||
AT_SYMLINK_FOLLOW :: 0x400
|
||||
AT_REMOVEDIR :: 0x200
|
||||
|
||||
flock :: struct {
|
||||
l_type: Lock_Type, /* [PSX] type of lock. */
|
||||
l_whence: c.short, /* [PSX] flag (Whence) of starting offset. */
|
||||
l_start: off_t, /* [PSX] relative offset in bytes. */
|
||||
l_len: off_t, /* [PSX] size; if 0 then until EOF. */
|
||||
l_pid: pid_t, /* [PSX] process ID of the process holding the lock. */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, openbsd, freebsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -53,6 +54,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
|
||||
FNM_PERIOD :: 0x04
|
||||
FNM_NOESCAPE :: 0x01
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
FNM_NOMATCH :: 1
|
||||
|
||||
FNM_PATHNAME :: 0x01
|
||||
FNM_NOESCAPE :: 0x02
|
||||
FNM_PERIOD :: 0x04
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -112,7 +113,7 @@ when ODIN_OS == .Darwin {
|
||||
|
||||
glob_t :: struct {
|
||||
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
|
||||
gl_matchc: c.size_t, /* count of paths matching pattern */
|
||||
gl_matchc: c.size_t, /* count of paths matching pattern */
|
||||
gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */
|
||||
gl_flags: Glob_Flags, /* copy of flags parameter to glob */
|
||||
gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
|
||||
@@ -144,7 +145,7 @@ when ODIN_OS == .Darwin {
|
||||
|
||||
glob_t :: struct {
|
||||
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
|
||||
gl_matchc: c.size_t, /* count of paths matching pattern */
|
||||
gl_matchc: c.size_t, /* count of paths matching pattern */
|
||||
gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */
|
||||
gl_flags: Glob_Flags, /* copy of flags parameter to glob */
|
||||
gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
|
||||
@@ -174,6 +175,33 @@ when ODIN_OS == .Darwin {
|
||||
GLOB_NOMATCH :: -3
|
||||
GLOB_NOSPACE :: -1
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
glob_t :: struct {
|
||||
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
|
||||
gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
|
||||
gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */
|
||||
gl_flags: Glob_Flags, /* copy of flags parameter to glob */
|
||||
|
||||
// Non-standard alternate file system access functions:
|
||||
|
||||
gl_closedir: proc "c" (dirp: DIR),
|
||||
gl_readdir: proc "c" (dirp: DIR) -> ^dirent,
|
||||
gl_opendir: proc "c" (path: cstring) -> DIR,
|
||||
gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result,
|
||||
gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result,
|
||||
}
|
||||
|
||||
GLOB_ERR :: 1 << 0
|
||||
GLOB_MARK :: 1 << 1
|
||||
GLOB_NOSORT :: 1 << 2
|
||||
GLOB_DOOFFS :: 1 << 3
|
||||
GLOB_NOCHECK :: 1 << 4
|
||||
GLOB_APPEND :: 1 << 5
|
||||
GLOB_NOESCAPE :: 1 << 6
|
||||
|
||||
GLOB_NOSPACE :: 1
|
||||
GLOB_ABORTED :: 2
|
||||
GLOB_NOMATCH :: 3
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -114,7 +115,7 @@ foreign lib {
|
||||
getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
|
||||
|
||||
gid_t :: distinct c.uint32_t
|
||||
|
||||
@@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
|
||||
gr_mem: [^]cstring, /* [PSX] group members */
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -238,7 +239,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
|
||||
ABDAY_4 :: 16
|
||||
ABDAY_5 :: 17
|
||||
ABDAY_6 :: 18
|
||||
ABDAY_7 :: 19
|
||||
ABDAY_7 :: 19
|
||||
|
||||
MON_1 :: 20
|
||||
MON_2 :: 21
|
||||
@@ -278,7 +279,91 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
|
||||
YESEXPR :: 47
|
||||
NOEXPR :: 49
|
||||
|
||||
CRNCYSTR :: 50
|
||||
CRNCYSTR :: 50
|
||||
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
|
||||
nl_item_t :: distinct c.int
|
||||
|
||||
// NOTE: All these values are set in an enum on the Linux implementation.
|
||||
// Some depend on locale.h contants (bits/locale.h to be precise).
|
||||
|
||||
// NOTE: ABDAY_1 is set to LC_TIME << 16 (LC_TIME is 2) on the enum group of
|
||||
// the Linux implementation.
|
||||
ABDAY_1 :: 0x20_000
|
||||
ABDAY_2 :: 0x20_001
|
||||
ABDAY_3 :: 0x20_002
|
||||
ABDAY_4 :: 0x20_003
|
||||
ABDAY_5 :: 0x20_004
|
||||
ABDAY_6 :: 0x20_005
|
||||
ABDAY_7 :: 0x20_006
|
||||
|
||||
DAY_1 :: 0x20_007
|
||||
DAY_2 :: 0x20_008
|
||||
DAY_3 :: 0x20_009
|
||||
DAY_4 :: 0x20_00A
|
||||
DAY_5 :: 0x20_00B
|
||||
DAY_6 :: 0x20_00C
|
||||
DAY_7 :: 0x20_00D
|
||||
|
||||
ABMON_1 :: 0x20_00E
|
||||
ABMON_2 :: 0x20_010
|
||||
ABMON_3 :: 0x20_011
|
||||
ABMON_4 :: 0x20_012
|
||||
ABMON_5 :: 0x20_013
|
||||
ABMON_6 :: 0x20_014
|
||||
ABMON_7 :: 0x20_015
|
||||
ABMON_8 :: 0x20_016
|
||||
ABMON_9 :: 0x20_017
|
||||
ABMON_10 :: 0x20_018
|
||||
ABMON_11 :: 0x20_019
|
||||
ABMON_12 :: 0x20_01A
|
||||
|
||||
MON_1 :: 0x20_01B
|
||||
MON_2 :: 0x20_01C
|
||||
MON_3 :: 0x20_01D
|
||||
MON_4 :: 0x20_01E
|
||||
MON_5 :: 0x20_020
|
||||
MON_6 :: 0x20_021
|
||||
MON_7 :: 0x20_022
|
||||
MON_8 :: 0x20_023
|
||||
MON_9 :: 0x20_024
|
||||
MON_10 :: 0x20_025
|
||||
MON_11 :: 0x20_026
|
||||
MON_12 :: 0x20_027
|
||||
|
||||
AM_STR :: 0x20_028
|
||||
PM_STR :: 0x20_029
|
||||
|
||||
D_T_FMT :: 0x20_02A
|
||||
D_FMT :: 0x20_02B
|
||||
T_FMT :: 0x20_02C
|
||||
T_FMT_AMPM :: 0x20_02D
|
||||
|
||||
ERA :: 0x20_02E
|
||||
ERA_D_FMT :: 0x20_030
|
||||
ALT_DIGITS :: 0x20_031
|
||||
ERA_D_T_FMT :: 0x20_032
|
||||
ERA_T_FMT :: 0x20_033
|
||||
|
||||
// NOTE: CODESET is the 16th member of the enum group starting with value
|
||||
// LC_CTYPE << 16, LC_CTYPE is 0.
|
||||
CODESET :: 0x0F
|
||||
|
||||
// NOTE: CRNCYSTR is the 16th member of the enum group starting with value
|
||||
// LC_MONETARY << 16, LC_MONETARY is 4.
|
||||
CRNCYSTR :: 0x40_00F
|
||||
|
||||
// NOTE: RADIXCHAR is the 1st member of the enum group starting with value
|
||||
// LC_NUMERIC << 16, LC_NUMERIC is 1.
|
||||
RADIXCHAR :: 0x10_000
|
||||
THOUSEP :: 0x10_001
|
||||
|
||||
// NOTE: YESEXPR is the 1st member of the enum group starting with value
|
||||
// LC_MESSAGES << 16, LC_MESSAGES is 5.
|
||||
YESEXPR :: 0x50_000
|
||||
NOEXPR :: 0x50_001
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
@@ -56,6 +57,7 @@ foreign lib {
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
|
||||
*/
|
||||
@(link_name=LBASENAME)
|
||||
basename :: proc(path: cstring) -> cstring ---
|
||||
|
||||
/*
|
||||
@@ -72,3 +74,9 @@ foreign lib {
|
||||
*/
|
||||
dirname :: proc(path: cstring) -> cstring ---
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
@(private) LBASENAME :: "__xpg_basename"
|
||||
} else {
|
||||
@(private) LBASENAME :: "basename"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user