update to master

This commit is contained in:
Colin Davidson
2024-11-20 15:51:08 -08:00
276 changed files with 15801 additions and 4509 deletions

View File

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

View File

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

@@ -266,6 +266,9 @@ bin/
*.exe
*.obj
*.pdb
*.res
desktop.ini
Thumbs.db
# - Linux/MacOS
odin

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
#+build !freestanding
#+build !freestanding, wasm32, wasm64p32
package mem
import "core:sync"

View File

@@ -1,4 +1,4 @@
#+build !freestanding
#+build !freestanding, wasm32, wasm64p32
package mem
import "base:runtime"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
#+build darwin, linux, freebsd, openbsd, netbsd
package posix
import "core:c"

View File

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

View File

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

View File

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

View File

@@ -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. */
}
}

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"

View File

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

View File

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