merge commit

This commit is contained in:
jason
2024-08-07 23:27:45 -04:00
148 changed files with 9509 additions and 2602 deletions
+5 -3
View File
@@ -18,10 +18,11 @@ jobs:
usesh: true usesh: true
copyback: false copyback: false
prepare: | prepare: |
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r | cut -d_ -f1)_${PKGSRC_BRANCH}/All" /usr/sbin/pkg_add pkgin PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/10.0_2024Q2/All" /usr/sbin/pkg_add pkgin
pkgin -y in gmake git bash python311 llvm clang pkgin -y in gmake git bash python311 llvm clang
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3 ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
run: | run: |
set -e -x
git config --global --add safe.directory $(pwd) git config --global --add safe.directory $(pwd)
gmake release gmake release
./odin version ./odin version
@@ -88,13 +89,13 @@ jobs:
- name: Download LLVM (MacOS Intel) - name: Download LLVM (MacOS Intel)
if: matrix.os == 'macos-13' if: matrix.os == 'macos-13'
run: | run: |
brew install llvm@17 brew install llvm@17 lua@5.4
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Download LLVM (MacOS ARM) - name: Download LLVM (MacOS ARM)
if: matrix.os == 'macos-14' if: matrix.os == 'macos-14'
run: | run: |
brew install llvm@17 wasmtime brew install llvm@17 wasmtime lua@5.4
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Build Odin - name: Build Odin
@@ -204,6 +205,7 @@ jobs:
shell: cmd shell: cmd
run: | run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
copy vendor\lua\5.4\windows\*.dll .
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
- name: Odin internals tests - name: Odin internals tests
shell: cmd shell: cmd
-1
View File
@@ -61,7 +61,6 @@ jobs:
mkdir dist mkdir dist
cp odin dist cp odin dist
cp LICENSE dist cp LICENSE dist
cp libLLVM* dist
cp -r shared dist cp -r shared dist
cp -r base dist cp -r base dist
cp -r core dist cp -r core dist
+31 -16
View File
@@ -42,8 +42,8 @@ overflow_add :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #option
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
add_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) --- saturating_add :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
sub_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) --- saturating_sub :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
@@ -227,6 +227,9 @@ simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) --- simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
simd_saturating_add :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
simd_saturating_sub :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
// Keeps Odin's Behaviour // Keeps Odin's Behaviour
// (x << y) if y <= mask else 0 // (x << y) if y <= mask else 0
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
@@ -237,9 +240,6 @@ simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_bit_and :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_bit_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_bit_or :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_bit_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_bit_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_bit_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
@@ -268,13 +268,28 @@ simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T --- simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T --- simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T --- simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T --- simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_min :: proc(a: #simd[N]T) -> T --- simd_reduce_min :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_max :: proc(a: #simd[N]T) -> T --- simd_reduce_max :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_and :: proc(a: #simd[N]T) -> T --- simd_reduce_and :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_or :: proc(a: #simd[N]T) -> T --- simd_reduce_or :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_xor :: proc(a: #simd[N]T) -> T --- simd_reduce_xor :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
simd_gather :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
simd_masked_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
simd_masked_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
simd_masked_expand_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
simd_masked_compress_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T --- simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
@@ -288,11 +303,11 @@ simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) --- simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0) // equivalent to a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T --- simd_lanes_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- simd_lanes_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- simd_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
// Checks if the current target supports the given target features. // Checks if the current target supports the given target features.
// //
+3 -2
View File
@@ -525,11 +525,12 @@ Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}
Linux, Linux,
Essence, Essence,
FreeBSD, FreeBSD,
Haiku,
OpenBSD, OpenBSD,
NetBSD, NetBSD,
Haiku,
WASI, WASI,
JS, JS,
Orca,
Freestanding, Freestanding,
} }
*/ */
@@ -589,7 +590,7 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
Memory = 1, Memory = 1,
Thread = 2, Thread = 2,
} }
Odin_Sanitizer_Flags :: distinct bitset[Odin_Sanitizer_Flag; u32] Odin_Sanitizer_Flags :: distinct bit_set[Odin_Sanitizer_Flag; u32]
ODIN_SANITIZER_FLAGS // is a constant ODIN_SANITIZER_FLAGS // is a constant
*/ */
+15 -8
View File
@@ -19,12 +19,15 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
// the pointer we return to the user. // the pointer we return to the user.
// //
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) { aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr, old_size: int, zero_memory := true) -> ([]byte, Allocator_Error) {
a := max(alignment, align_of(rawptr)) a := max(alignment, align_of(rawptr))
space := size + a - 1 space := size + a - 1
allocated_mem: rawptr allocated_mem: rawptr
if old_ptr != nil {
force_copy := old_ptr != nil && a > align_of(rawptr)
if !force_copy && old_ptr != nil {
original_old_ptr := ([^]rawptr)(old_ptr)[-1] original_old_ptr := ([^]rawptr)(old_ptr)[-1]
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
} else { } else {
@@ -36,12 +39,19 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
diff := int(aligned_ptr - ptr) diff := int(aligned_ptr - ptr)
if (size + diff) > space || allocated_mem == nil { if (size + diff) > space || allocated_mem == nil {
aligned_free(old_ptr)
aligned_free(allocated_mem)
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
} }
aligned_mem = rawptr(aligned_ptr) aligned_mem = rawptr(aligned_ptr)
([^]rawptr)(aligned_mem)[-1] = allocated_mem ([^]rawptr)(aligned_mem)[-1] = allocated_mem
if force_copy {
mem_copy_non_overlapping(aligned_mem, old_ptr, old_size)
aligned_free(old_ptr)
}
return byte_slice(aligned_mem, size), nil return byte_slice(aligned_mem, size), nil
} }
@@ -53,10 +63,10 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) { aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) {
if p == nil { if p == nil {
return nil, nil return aligned_alloc(new_size, new_alignment, nil, old_size, zero_memory)
} }
new_memory = aligned_alloc(new_size, new_alignment, p, zero_memory) or_return new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return
// NOTE: heap_resize does not zero the new memory, so we do it // NOTE: heap_resize does not zero the new memory, so we do it
if zero_memory && new_size > old_size { if zero_memory && new_size > old_size {
@@ -68,7 +78,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
switch mode { switch mode {
case .Alloc, .Alloc_Non_Zeroed: case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment, nil, mode == .Alloc) return aligned_alloc(size, alignment, nil, 0, mode == .Alloc)
case .Free: case .Free:
aligned_free(old_memory) aligned_free(old_memory)
@@ -77,9 +87,6 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, .Mode_Not_Implemented return nil, .Mode_Not_Implemented
case .Resize, .Resize_Non_Zeroed: case .Resize, .Resize_Non_Zeroed:
if old_memory == nil {
return aligned_alloc(size, alignment, nil, mode == .Resize)
}
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize) return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
case .Query_Features: case .Query_Features:
+1 -1
View File
@@ -95,7 +95,7 @@ Linux)
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)" LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
# Copy libLLVM*.so into current directory for linking # Copy libLLVM*.so into current directory for linking
# NOTE: This is needed by the Linux release pipeline! # NOTE: This is needed by the Linux release pipeline!
cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./ # cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN" LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
;; ;;
OpenBSD) OpenBSD)
+3 -3
View File
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
@(private) @(private)
_rand_bytes :: proc(dst: []byte) { _rand_bytes :: proc(dst: []byte) {
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)) ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != os.ERROR_NONE { if ret != nil {
switch ret { #partial switch ret {
case os.ERROR_INVALID_HANDLE: case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid. // The handle to the first parameter is invalid.
// This should not happen here, since we explicitly pass nil to it // This should not happen here, since we explicitly pass nil to it
+3 -3
View File
@@ -38,9 +38,9 @@ iterate_csv_from_stream :: proc(filename: string) {
r.reuse_record_buffer = true // Without it you have to each of the fields within it r.reuse_record_buffer = true // Without it you have to each of the fields within it
defer csv.reader_destroy(&r) defer csv.reader_destroy(&r)
handle, errno := os.open(filename) handle, err := os.open(filename)
if errno != os.ERROR_NONE { if err != nil {
fmt.printfln("Error opening file: %v", filename) fmt.eprintfln("Error opening file: %v", filename)
return return
} }
defer os.close(handle) defer os.close(handle)
+1 -1
View File
@@ -28,7 +28,7 @@ Parse_Error :: struct {
// Provides more granular information than what just a string could hold. // Provides more granular information than what just a string could hold.
Open_File_Error :: struct { Open_File_Error :: struct {
filename: string, filename: string,
errno: os.Errno, errno: os.Error,
mode: int, mode: int,
perms: int, perms: int,
} }
+2 -2
View File
@@ -254,8 +254,8 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
} }
handle, errno := os.open(str, mode, perms) handle, errno := os.open(str, mode, perms)
if errno != 0 { if errno != nil {
// NOTE(Feoramund): os.Errno is system-dependent, and there's // NOTE(Feoramund): os.Error is system-dependent, and there's
// currently no good way to translate them all into strings. // currently no good way to translate them all into strings.
// //
// The upcoming `os2` package will hopefully solve this. // The upcoming `os2` package will hopefully solve this.
+65 -2
View File
@@ -112,7 +112,8 @@ Image_Option:
`.alpha_drop_if_present` `.alpha_drop_if_present`
If the image has an alpha channel, drop it. If the image has an alpha channel, drop it.
You may want to use `.alpha_premultiply` in this case. You may want to use `.alpha_
tiply` in this case.
NOTE: For PNG, this also skips handling of the tRNS chunk, if present, NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
unless you select `alpha_premultiply`. unless you select `alpha_premultiply`.
@@ -587,6 +588,32 @@ Channel :: enum u8 {
A = 4, A = 4,
} }
// Take a slice of pixels (`[]RGBA_Pixel`, etc), and return an `Image`
// Don't call `destroy` on the resulting `Image`. Instead, delete the original `pixels` slice.
pixels_to_image :: proc(pixels: [][$N]$E, width: int, height: int) -> (img: Image, ok: bool) where E == u8 || E == u16, N >= 1, N <= 4 {
if len(pixels) != width * height {
return {}, false
}
img.height = height
img.width = width
img.depth = 8 when E == u8 else 16
img.channels = N
s := transmute(runtime.Raw_Slice)pixels
d := runtime.Raw_Dynamic_Array{
data = s.data,
len = s.len * size_of(E) * N,
cap = s.len * size_of(E) * N,
allocator = runtime.nil_allocator(),
}
img.pixels = bytes.Buffer{
buf = transmute([dynamic]u8)d,
}
return img, true
}
// When you have an RGB(A) image, but want a particular channel. // When you have an RGB(A) image, but want a particular channel.
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) { return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
// Were we actually given a valid image? // Were we actually given a valid image?
@@ -1266,7 +1293,7 @@ blend_single_channel :: #force_inline proc(fg, alpha, bg: $T) -> (res: T) where
return T(c & (MAX - 1)) return T(c & (MAX - 1))
} }
blend_pixel :: #force_inline proc(fg: [$N]$T, alpha: T, bg: [N]T) -> (res: [N]T) where (T == u8 || T == u16), N >= 1 && N <= 4 { blend_pixel :: #force_inline proc(fg: [$N]$T, alpha: T, bg: [N]T) -> (res: [N]T) where (T == u8 || T == u16), N >= 1, N <= 4 {
MAX :: 256 when T == u8 else 65536 MAX :: 256 when T == u8 else 65536
when N == 1 { when N == 1 {
@@ -1293,6 +1320,42 @@ blend_pixel :: #force_inline proc(fg: [$N]$T, alpha: T, bg: [N]T) -> (res: [N]T)
} }
blend :: proc{blend_single_channel, blend_pixel} blend :: proc{blend_single_channel, blend_pixel}
// For all pixels of the image, multiplies R, G and B by Alpha. This is useful mainly for games rendering anti-aliased transparent sprites.
// Grayscale with alpha images are supported as well.
// Note that some image formats like QOI explicitly do NOT support premultiplied alpha, so you will end up with a non-standard file.
premultiply_alpha :: proc(img: ^Image) -> (ok: bool) {
switch {
case img.channels == 2 && img.depth == 8:
pixels := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u8(u32(pixel.r) * u32(pixel.g) / 0xFF)
}
return true
case img.channels == 2 && img.depth == 16:
pixels := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u16(u32(pixel.r) * u32(pixel.g) / 0xFFFF)
}
return true
case img.channels == 4 && img.depth == 8:
pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u8(u32(pixel.r) * u32(pixel.a) / 0xFF)
pixel.g = u8(u32(pixel.g) * u32(pixel.a) / 0xFF)
pixel.b = u8(u32(pixel.b) * u32(pixel.a) / 0xFF)
}
return true
case img.channels == 4 && img.depth == 16:
pixels := mem.slice_data_cast([]RGBA_Pixel_16, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u16(u32(pixel.r) * u32(pixel.a) / 0xFFFF)
pixel.g = u16(u32(pixel.g) * u32(pixel.a) / 0xFFFF)
pixel.b = u16(u32(pixel.b) * u32(pixel.a) / 0xFFFF)
}
return true
case: return false
}
}
// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate. // Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
// Returns early with `false` if already an RGB(A) image. // Returns early with `false` if already an RGB(A) image.
+1 -1
View File
@@ -27,7 +27,7 @@ which :: proc{
which_file :: proc(path: string) -> Which_File_Type { which_file :: proc(path: string) -> Which_File_Type {
f, err := os.open(path) f, err := os.open(path)
if err != 0 { if err != nil {
return .Unknown return .Unknown
} }
header: [128]byte header: [128]byte
+1 -1
View File
@@ -213,7 +213,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
} }
fd, err := open(filename, flags, mode) fd, err := open(filename, flags, mode)
if err != 0 { if err != nil {
return false return false
} }
defer close(fd) defer close(fd)
+1 -1
View File
@@ -450,7 +450,7 @@ when false {
} }
fd, fderr := open(filename, flags, mode) fd, fderr := open(filename, flags, mode)
if fderr != 0 { if fderr != nil {
return .Cannot_Open_File return .Cannot_Open_File
} }
defer close(fd) defer close(fd)
+2 -1
View File
@@ -275,7 +275,8 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E { vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
a0 := normalize0(a) a0 := normalize0(a)
b0 := normalize0(b) b0 := normalize0(b)
return math.acos(dot(a0, b0)) d := clamp(dot(a0, b0), -1, +1)
return math.acos(d)
} }
quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 { quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 {
c := normalize0(conj(a) * b) c := normalize0(conj(a) * b)
+1 -1
View File
@@ -58,7 +58,7 @@ Example:
import "core:fmt" import "core:fmt"
set_global_seed_example :: proc() { set_global_seed_example :: proc() {
rand.set_global_seed(1) rand.reset(1)
fmt.println(rand.uint64()) fmt.println(rand.uint64())
} }
+2 -2
View File
@@ -24,7 +24,7 @@ map_file :: proc{
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
fd, err := os.open(filename, os.O_RDWR) fd, err := os.open(filename, os.O_RDWR)
if err != 0 { if err != nil {
return nil, .Open_Failure return nil, .Open_Failure
} }
defer os.close(fd) defer os.close(fd)
@@ -34,7 +34,7 @@ map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []
map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
size, os_err := os.file_size(os.Handle(fd)) size, os_err := os.file_size(os.Handle(fd))
if os_err != 0 { if os_err != nil {
return nil, .Stat_Failure return nil, .Stat_Failure
} }
if size < 0 { if size < 0 {
+40 -32
View File
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
unreachable() unreachable()
} }
sock, ok := os.socket(c_family, c_type, c_protocol) sock, sock_err := os.socket(c_family, c_type, c_protocol)
if ok != os.ERROR_NONE { if sock_err != nil {
err = Create_Socket_Error(ok) err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
return return
} }
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint) sockaddr := _endpoint_to_sockaddr(endpoint)
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE { if res != nil {
err = Dial_Error(res) err = Dial_Error(os.is_platform_error(res) or_else -1)
return return
} }
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
sockaddr := _endpoint_to_sockaddr(ep) sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt) s := any_socket_to_socket(skt)
res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE { if res != nil {
if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT { if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
err = .Privileged_Port_Without_Root err = .Privileged_Port_Without_Root
} else { } else {
err = Bind_Error(res) err = Bind_Error(os.is_platform_error(res) or_else -1)
} }
} }
return return
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
bind(sock, interface_endpoint) or_return bind(sock, interface_endpoint) or_return
res := os.listen(os.Socket(skt), backlog) res := os.listen(os.Socket(skt), backlog)
if res != os.ERROR_NONE { if res != nil {
err = Listen_Error(res) err = Listen_Error(os.is_platform_error(res) or_else -1)
return return
} }
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
sockaddr: os.SOCKADDR_STORAGE_LH sockaddr: os.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr)) sockaddrlen := c.int(size_of(sockaddr))
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen) client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
if ok != os.ERROR_NONE { if client_sock_err != nil {
err = Accept_Error(ok) err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
return return
} }
client = TCP_Socket(client_sock) client = TCP_Socket(client_sock)
@@ -162,9 +162,9 @@ _recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Networ
if len(buf) <= 0 { if len(buf) <= 0 {
return return
} }
res, ok := os.recv(os.Socket(skt), buf, 0) res, res_err := os.recv(os.Socket(skt), buf, 0)
if ok != os.ERROR_NONE { if res_err != nil {
err = TCP_Recv_Error(ok) err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
return int(res), nil return int(res), nil
@@ -178,9 +178,9 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
from: os.SOCKADDR_STORAGE_LH from: os.SOCKADDR_STORAGE_LH
fromsize := c.int(size_of(from)) fromsize := c.int(size_of(from))
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize) res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
if ok != os.ERROR_NONE { if res_err != nil {
err = UDP_Recv_Error(ok) err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
@@ -194,9 +194,13 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
for bytes_written < len(buf) { for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written) limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit] remaining := buf[bytes_written:][:limit]
res, ok := os.send(os.Socket(skt), remaining, 0) res, res_err := os.send(os.Socket(skt), remaining, os.MSG_NOSIGNAL)
if ok != os.ERROR_NONE { if res_err == os.EPIPE {
err = TCP_Send_Error(ok) // EPIPE arises if the socket has been closed remotely.
err = TCP_Send_Error.Connection_Closed
return
} else if res_err != nil {
err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
bytes_written += int(res) bytes_written += int(res)
@@ -210,9 +214,13 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
for bytes_written < len(buf) { for bytes_written < len(buf) {
limit := min(1<<31, len(buf) - bytes_written) limit := min(1<<31, len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit] remaining := buf[bytes_written:][:limit]
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len)) res, res_err := os.sendto(os.Socket(skt), remaining, os.MSG_NOSIGNAL, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
if ok != os.ERROR_NONE { if res_err == os.EPIPE {
err = UDP_Send_Error(ok) // EPIPE arises if the socket has been closed remotely.
err = UDP_Send_Error.Not_Socket
return
} else if res_err != nil {
err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
bytes_written += int(res) bytes_written += int(res)
@@ -224,8 +232,8 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { _shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
s := any_socket_to_socket(skt) s := any_socket_to_socket(skt)
res := os.shutdown(os.Socket(s), int(manner)) res := os.shutdown(os.Socket(s), int(manner))
if res != os.ERROR_NONE { if res != nil {
return Shutdown_Error(res) return Shutdown_Error(os.is_platform_error(res) or_else -1)
} }
return return
} }
@@ -302,8 +310,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
skt := any_socket_to_socket(s) skt := any_socket_to_socket(s)
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len) res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
if res != os.ERROR_NONE { if res != nil {
return Socket_Option_Error(res) return Socket_Option_Error(os.is_platform_error(res) or_else -1)
} }
return nil return nil
@@ -314,8 +322,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
socket := any_socket_to_socket(socket) socket := any_socket_to_socket(socket)
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0) flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
if getfl_err != os.ERROR_NONE { if getfl_err != nil {
return Set_Blocking_Error(getfl_err) return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
} }
if should_block { if should_block {
@@ -325,8 +333,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
} }
_, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags) _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
if setfl_err != os.ERROR_NONE { if setfl_err != nil {
return Set_Blocking_Error(setfl_err) return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
} }
return nil return nil
+1 -1
View File
@@ -757,7 +757,7 @@ Array_Type :: struct {
using node: Expr, using node: Expr,
open: tokenizer.Pos, open: tokenizer.Pos,
tag: ^Expr, tag: ^Expr,
len: ^Expr, // Ellipsis node for [?]T array types, nil for slice types len: ^Expr, // Unary_Expr node for [?]T array types, nil for slice types
close: tokenizer.Pos, close: tokenizer.Pos,
elem: ^Expr, elem: ^Expr,
} }
+2 -1
View File
@@ -1778,6 +1778,7 @@ parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr {
type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok)) type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok))
} }
e := ast.new(ast.Ellipsis, type.pos, type) e := ast.new(ast.Ellipsis, type.pos, type)
e.tok = tok.kind
e.expr = type e.expr = type
return e return e
} }
@@ -2844,7 +2845,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p) close := expect_closing_brace_of_field_list(p)
bf := ast.new(ast.Bit_Field_Type, tok.pos, close.pos) bf := ast.new(ast.Bit_Field_Type, tok.pos, end_pos(close))
bf.tok_pos = tok.pos bf.tok_pos = tok.pos
bf.backing_type = backing_type bf.backing_type = backing_type
-73
View File
@@ -1,73 +0,0 @@
//+build freebsd, netbsd
package os
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
fullpath := make([]byte, len(dirpath)+1+len(filename), context.temp_allocator)
copy(fullpath, dirpath)
copy(fullpath[len(dirpath):], "/")
copy(fullpath[len(dirpath)+1:], filename)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(string(fullpath), allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
-69
View File
@@ -1,69 +0,0 @@
package os
import "core:strings"
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
-72
View File
@@ -1,72 +0,0 @@
package os
import "core:strings"
import "core:mem"
import "base:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
-71
View File
@@ -1,71 +0,0 @@
package os
import "core:strings"
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
// XXX OpenBSD
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
+62
View File
@@ -0,0 +1,62 @@
//+build darwin, linux, netbsd, freebsd, openbsd
package os
import "core:strings"
@(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
dirp := _fdopendir(fd) or_return
defer _closedir(dirp)
dirpath := absolute_path_from_handle(fd) or_return
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator) or_return
defer if err != nil {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
}
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != nil {
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := string(cstring(&entry.name[0]))
if filename == "." || filename == ".." {
continue
}
fullpath := strings.join({ dirpath, filename }, "/", allocator)
s: OS_Stat
s, err = _lstat(fullpath)
if err != nil {
delete(fullpath, allocator)
return
}
_fill_file_info_from_stat(&fi_, s)
fi_.fullpath = fullpath
fi_.name = path_base(fi_.fullpath)
append(&dfi, fi_)
}
return dfi[:], nil
}
+11 -10
View File
@@ -4,7 +4,9 @@ import win32 "core:sys/windows"
import "core:strings" import "core:strings"
import "base:runtime" import "base:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { @(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
@(require_results)
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) { find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".." // Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 { if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
@@ -57,7 +59,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
dir_fi, _ := file_info_from_get_file_information_by_handle("", h) dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
if !dir_fi.is_dir { if !dir_fi.is_dir {
return nil, ERROR_FILE_IS_NOT_DIR return nil, .Not_Dir
} }
n := n n := n
@@ -68,15 +70,14 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath: []u16 wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator) if len(wpath) == 0 {
if len(wpath) == 0 || err != ERROR_NONE {
return return
} }
dfi := make([dynamic]File_Info, 0, size) dfi := make([dynamic]File_Info, 0, size) or_return
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
copy(wpath_search, wpath) copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\' wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*' wpath_search[len(wpath)+1] = '*'
@@ -88,7 +89,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
find_data := &win32.WIN32_FIND_DATAW{} find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE { if find_handle == win32.INVALID_HANDLE_VALUE {
err = Errno(win32.GetLastError()) err = get_last_error()
return dfi[:], err return dfi[:], err
} }
defer win32.FindClose(find_handle) defer win32.FindClose(find_handle)
@@ -101,7 +102,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
if !win32.FindNextFileW(find_handle, find_data) { if !win32.FindNextFileW(find_handle, find_data) {
e := Errno(win32.GetLastError()) e := get_last_error()
if e == ERROR_NO_MORE_FILES { if e == ERROR_NO_MORE_FILES {
break break
} }
@@ -109,5 +110,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
} }
return dfi[:], ERROR_NONE return dfi[:], nil
} }
+17 -17
View File
@@ -7,28 +7,23 @@ import "base:runtime"
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false // Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" { if key == "" {
return return
} }
wkey := win32.utf8_to_wstring(key) wkey := win32.utf8_to_wstring(key)
n := win32.GetEnvironmentVariableW(wkey, nil, 0) n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 { if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
err := win32.GetLastError()
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false return "", false
} }
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
b := make([dynamic]u16, n, context.temp_allocator) b, _ := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 { if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
err := win32.GetLastError()
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false return "", false
} }
}
value, _ = win32.utf16_to_utf8(b[:n], allocator) value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true found = true
return return
@@ -39,41 +34,46 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
// It returns the value, which will be empty if the variable is not present // It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env // To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
// set_env sets the value of the environment variable named by the key // set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Errno { set_env :: proc(key, value: string) -> Error {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
v := win32.utf8_to_wstring(value) v := win32.utf8_to_wstring(value)
if !win32.SetEnvironmentVariableW(k, v) { if !win32.SetEnvironmentVariableW(k, v) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return 0 return nil
} }
// unset_env unsets a single environment variable // unset_env unsets a single environment variable
unset_env :: proc(key: string) -> Errno { unset_env :: proc(key: string) -> Error {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
if !win32.SetEnvironmentVariableW(k, nil) { if !win32.SetEnvironmentVariableW(k, nil) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return 0 return nil
} }
// environ returns a copy of strings representing the environment, in the form "key=value" // environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator // NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator := context.allocator) -> []string { environ :: proc(allocator := context.allocator) -> []string {
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW()) envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
if envs == nil { if envs == nil {
return nil return nil
} }
defer win32.FreeEnvironmentStringsW(envs) defer win32.FreeEnvironmentStringsW(envs)
r := make([dynamic]string, 0, 50, allocator) r, err := make([dynamic]string, 0, 50, allocator)
if err != nil {
return nil
}
for from, i := 0, 0; true; i += 1 { for from, i := 0, 0; true; i += 1 {
if c := envs[i]; c == 0 { if c := envs[i]; c == 0 {
if i <= from { if i <= from {
+322
View File
@@ -0,0 +1,322 @@
package os
import "base:intrinsics"
import "base:runtime"
import "core:io"
Platform_Error :: _Platform_Error
#assert(size_of(Platform_Error) <= 4)
#assert(intrinsics.type_has_nil(Platform_Error))
General_Error :: enum u32 {
None,
Permission_Denied,
Exist,
Not_Exist,
Closed,
Timeout,
Broken_Pipe,
// Indicates that an attempt to retrieve a file's size was made, but the
// file doesn't have a size.
No_Size,
Invalid_File,
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Pattern_Has_Separator,
Unsupported,
File_Is_Pipe,
Not_Dir,
}
Errno :: Error // alias for legacy use
Error :: union #shared_nil {
General_Error,
io.Error,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == 8)
ERROR_NONE :: Error{}
ERROR_EOF :: io.Error.EOF
@(require_results)
is_platform_error :: proc "contextless" (ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
@(require_results)
error_string :: proc "contextless" (ferr: Error) -> string {
if ferr == nil {
return ""
}
switch e in ferr {
case General_Error:
switch e {
case .None: return ""
case .Permission_Denied: return "permission denied"
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Closed: return "file already closed"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .No_Size: return "file has no definite size"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
}
case io.Error:
switch e {
case .None: return ""
case .EOF: return "eof"
case .Unexpected_EOF: return "unexpected eof"
case .Short_Write: return "short write"
case .Invalid_Write: return "invalid write result"
case .Short_Buffer: return "short buffer"
case .No_Progress: return "multiple read calls return no data or error"
case .Invalid_Whence: return "invalid whence"
case .Invalid_Offset: return "invalid offset"
case .Invalid_Unread: return "invalid unread"
case .Negative_Read: return "negative read"
case .Negative_Write: return "negative write"
case .Negative_Count: return "negative count"
case .Buffer_Full: return "buffer full"
case .Unknown, .Empty: //
}
case runtime.Allocator_Error:
switch e {
case .None: return ""
case .Out_Of_Memory: return "out of memory"
case .Invalid_Pointer: return "invalid allocator pointer"
case .Invalid_Argument: return "invalid allocator argument"
case .Mode_Not_Implemented: return "allocator mode not implemented"
}
case Platform_Error:
return _error_string(e)
}
return "unknown error"
}
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: Error) {
err_str := error_string(ferr)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf_ := intrinsics.alloca(length, 1)
buf := buf_[:length]
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
buf[length - 1] = '\n'
return write(f, buf)
}
@(require_results, private)
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
if e == nil {
return ""
}
when ODIN_OS == .Darwin {
if s := string(_darwin_string_error(i32(e))); s != "" {
return s
}
}
when ODIN_OS != .Linux {
@(require_results)
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
n := len(array)
left, right := 0, n
for left < right {
mid := int(uint(left+right) >> 1)
if array[mid] < key {
left = mid+1
} else {
// equal or greater
right = mid
}
}
return left, left < n && array[left] == key
}
err := runtime.Type_Info_Enum_Value(e)
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
if idx, ok := binary_search(ti.values, err); ok {
return ti.names[idx]
}
} else {
@(rodata, static)
pe_strings := [Platform_Error]string{
.NONE = "",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.ESRCH = "No such process",
.EINTR = "Interrupted system call",
.EIO = "Input/output error",
.ENXIO = "No such device or address",
.E2BIG = "Argument list too long",
.ENOEXEC = "Exec format error",
.EBADF = "Bad file descriptor",
.ECHILD = "No child processes",
.EAGAIN = "Resource temporarily unavailable",
.ENOMEM = "Cannot allocate memory",
.EACCES = "Permission denied",
.EFAULT = "Bad address",
.ENOTBLK = "Block device required",
.EBUSY = "Device or resource busy",
.EEXIST = "File exists",
.EXDEV = "Invalid cross-device link",
.ENODEV = "No such device",
.ENOTDIR = "Not a directory",
.EISDIR = "Is a directory",
.EINVAL = "Invalid argument",
.ENFILE = "Too many open files in system",
.EMFILE = "Too many open files",
.ENOTTY = "Inappropriate ioctl for device",
.ETXTBSY = "Text file busy",
.EFBIG = "File too large",
.ENOSPC = "No space left on device",
.ESPIPE = "Illegal seek",
.EROFS = "Read-only file system",
.EMLINK = "Too many links",
.EPIPE = "Broken pipe",
.EDOM = "Numerical argument out of domain",
.ERANGE = "Numerical result out of range",
.EDEADLK = "Resource deadlock avoided",
.ENAMETOOLONG = "File name too long",
.ENOLCK = "No locks available",
.ENOSYS = "Function not implemented",
.ENOTEMPTY = "Directory not empty",
.ELOOP = "Too many levels of symbolic links",
.EUNKNOWN_41 = "Unknown Error (41)",
.ENOMSG = "No message of desired type",
.EIDRM = "Identifier removed",
.ECHRNG = "Channel number out of range",
.EL2NSYNC = "Level 2 not synchronized",
.EL3HLT = "Level 3 halted",
.EL3RST = "Level 3 reset",
.ELNRNG = "Link number out of range",
.EUNATCH = "Protocol driver not attached",
.ENOCSI = "No CSI structure available",
.EL2HLT = "Level 2 halted",
.EBADE = "Invalid exchange",
.EBADR = "Invalid request descriptor",
.EXFULL = "Exchange full",
.ENOANO = "No anode",
.EBADRQC = "Invalid request code",
.EBADSLT = "Invalid slot",
.EUNKNOWN_58 = "Unknown Error (58)",
.EBFONT = "Bad font file format",
.ENOSTR = "Device not a stream",
.ENODATA = "No data available",
.ETIME = "Timer expired",
.ENOSR = "Out of streams resources",
.ENONET = "Machine is not on the network",
.ENOPKG = "Package not installed",
.EREMOTE = "Object is remote",
.ENOLINK = "Link has been severed",
.EADV = "Advertise error",
.ESRMNT = "Srmount error",
.ECOMM = "Communication error on send",
.EPROTO = "Protocol error",
.EMULTIHOP = "Multihop attempted",
.EDOTDOT = "RFS specific error",
.EBADMSG = "Bad message",
.EOVERFLOW = "Value too large for defined data type",
.ENOTUNIQ = "Name not unique on network",
.EBADFD = "File descriptor in bad state",
.EREMCHG = "Remote address changed",
.ELIBACC = "Can not access a needed shared library",
.ELIBBAD = "Accessing a corrupted shared library",
.ELIBSCN = ".lib section in a.out corrupted",
.ELIBMAX = "Attempting to link in too many shared libraries",
.ELIBEXEC = "Cannot exec a shared library directly",
.EILSEQ = "Invalid or incomplete multibyte or wide character",
.ERESTART = "Interrupted system call should be restarted",
.ESTRPIPE = "Streams pipe error",
.EUSERS = "Too many users",
.ENOTSOCK = "Socket operation on non-socket",
.EDESTADDRREQ = "Destination address required",
.EMSGSIZE = "Message too long",
.EPROTOTYPE = "Protocol wrong type for socket",
.ENOPROTOOPT = "Protocol not available",
.EPROTONOSUPPORT = "Protocol not supported",
.ESOCKTNOSUPPORT = "Socket type not supported",
.EOPNOTSUPP = "Operation not supported",
.EPFNOSUPPORT = "Protocol family not supported",
.EAFNOSUPPORT = "Address family not supported by protocol",
.EADDRINUSE = "Address already in use",
.EADDRNOTAVAIL = "Cannot assign requested address",
.ENETDOWN = "Network is down",
.ENETUNREACH = "Network is unreachable",
.ENETRESET = "Network dropped connection on reset",
.ECONNABORTED = "Software caused connection abort",
.ECONNRESET = "Connection reset by peer",
.ENOBUFS = "No buffer space available",
.EISCONN = "Transport endpoint is already connected",
.ENOTCONN = "Transport endpoint is not connected",
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
.ETOOMANYREFS = "Too many references: cannot splice",
.ETIMEDOUT = "Connection timed out",
.ECONNREFUSED = "Connection refused",
.EHOSTDOWN = "Host is down",
.EHOSTUNREACH = "No route to host",
.EALREADY = "Operation already in progress",
.EINPROGRESS = "Operation now in progress",
.ESTALE = "Stale file handle",
.EUCLEAN = "Structure needs cleaning",
.ENOTNAM = "Not a XENIX named type file",
.ENAVAIL = "No XENIX semaphores available",
.EISNAM = "Is a named type file",
.EREMOTEIO = "Remote I/O error",
.EDQUOT = "Disk quota exceeded",
.ENOMEDIUM = "No medium found",
.EMEDIUMTYPE = "Wrong medium type",
.ECANCELED = "Operation canceled",
.ENOKEY = "Required key not available",
.EKEYEXPIRED = "Key has expired",
.EKEYREVOKED = "Key has been revoked",
.EKEYREJECTED = "Key was rejected by service",
.EOWNERDEAD = "Owner died",
.ENOTRECOVERABLE = "State not recoverable",
.ERFKILL = "Operation not possible due to RF-kill",
.EHWPOISON = "Memory page has hardware error",
}
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
return pe_strings[e]
}
}
return "<unknown platform error>"
}
@(private, require_results)
error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None
}
return ferr.(io.Error) or_else .Unknown
}
+83 -93
View File
@@ -5,13 +5,15 @@ import "base:intrinsics"
import "base:runtime" import "base:runtime"
import "core:unicode/utf16" import "core:unicode/utf16"
@(require_results)
is_path_separator :: proc(c: byte) -> bool { is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\' return c == '/' || c == '\\'
} }
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
if len(path) == 0 { if len(path) == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND return INVALID_HANDLE, General_Error.Not_Exist
} }
access: u32 access: u32
@@ -52,32 +54,31 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
wide_path := win32.utf8_to_wstring(path) wide_path := win32.utf8_to_wstring(path)
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil)) handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
if handle != INVALID_HANDLE { if handle != INVALID_HANDLE {
return handle, ERROR_NONE return handle, nil
} }
err := Errno(win32.GetLastError()) return INVALID_HANDLE, get_last_error()
return INVALID_HANDLE, err
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
if !win32.CloseHandle(win32.HANDLE(fd)) { if !win32.CloseHandle(win32.HANDLE(fd)) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return ERROR_NONE return nil
} }
flush :: proc(fd: Handle) -> (err: Errno) { flush :: proc(fd: Handle) -> (err: Error) {
if !win32.FlushFileBuffers(win32.HANDLE(fd)) { if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
single_write_length: win32.DWORD single_write_length: win32.DWORD
@@ -90,25 +91,24 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil) e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
if single_write_length <= 0 || !e { if single_write_length <= 0 || !e {
err := Errno(win32.GetLastError()) return int(total_write), get_last_error()
return int(total_write), err
} }
total_write += i64(single_write_length) total_write += i64(single_write_length)
} }
return int(total_write), ERROR_NONE return int(total_write), nil
} }
@(private="file") @(private="file", require_results)
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 { if len(b) == 0 {
return 0, 0 return 0, nil
} }
BUF_SIZE :: 386 BUF_SIZE :: 386
buf16: [BUF_SIZE]u16 buf16: [BUF_SIZE]u16
buf8: [4*BUF_SIZE]u8 buf8: [4*BUF_SIZE]u8
for n < len(b) && err == 0 { for n < len(b) && err == nil {
min_read := max(len(b)/4, 1 if len(b) > 0 else 0) min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
max_read := u32(min(BUF_SIZE, min_read)) max_read := u32(min(BUF_SIZE, min_read))
if max_read == 0 { if max_read == 0 {
@@ -118,7 +118,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
single_read_length: u32 single_read_length: u32
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
if !ok { if !ok {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
@@ -149,9 +149,9 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
return return
} }
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) { read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
handle := win32.HANDLE(fd) handle := win32.HANDLE(fd)
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
if is_console { if is_console {
total_read, err = read_console(handle, data[total_read:][:to_read]) total_read, err = read_console(handle, data[total_read:][:to_read])
if err != 0 { if err != nil {
return total_read, err return total_read, err
} }
} else { } else {
@@ -175,18 +175,18 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
// Successful read can mean two things, including EOF, see: // Successful read can mean two things, including EOF, see:
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
if bytes_read == 0 { if bytes_read == 0 {
return 0, ERROR_HANDLE_EOF return 0, .EOF
} else { } else {
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
} else { } else {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
} }
return total_read, ERROR_NONE return total_read, nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
w: u32 w: u32
switch whence { switch whence {
case 0: w = win32.FILE_BEGIN case 0: w = win32.FILE_BEGIN
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
lo := i32(offset) lo := i32(offset)
ft := win32.GetFileType(win32.HANDLE(fd)) ft := win32.GetFileType(win32.HANDLE(fd))
if ft == win32.FILE_TYPE_PIPE { if ft == win32.FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE return 0, .File_Is_Pipe
} }
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w) dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
if dw_ptr == win32.INVALID_SET_FILE_POINTER { if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := Errno(win32.GetLastError()) err := get_last_error()
return 0, err return 0, err
} }
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE return i64(hi)<<32 + i64(dw_ptr), nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
length: win32.LARGE_INTEGER length: win32.LARGE_INTEGER
err: Errno err: Error
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return i64(length), err return i64(length), err
} }
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
@(private) @(private)
MAX_RW :: 1<<30 MAX_RW :: 1<<30
ERROR_EOF :: 38
@(private) @(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data buf := data
if len(buf) > MAX_RW { if len(buf) > MAX_RW {
buf = buf[:MAX_RW] buf = buf[:MAX_RW]
@@ -239,15 +239,15 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
done: win32.DWORD done: win32.DWORD
e: Errno e: Error
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError()) e = get_last_error()
done = 0 done = 0
} }
return int(done), e return int(done), e
} }
@(private) @(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data buf := data
if len(buf) > MAX_RW { if len(buf) > MAX_RW {
buf = buf[:MAX_RW] buf = buf[:MAX_RW]
@@ -261,9 +261,9 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
done: win32.DWORD done: win32.DWORD
e: Errno e: Error
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError()) e = get_last_error()
done = 0 done = 0
} }
return int(done), e return int(done), e
@@ -279,19 +279,19 @@ on Windows, read_at changes the position of the file cursor, on *nix, it does no
will read from the location twice on *nix, and from two different locations on Windows will read from the location twice on *nix, and from two different locations on Windows
*/ */
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 { if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET return 0, .Invalid_Offset
} }
b, offset := data, offset b, offset := data, offset
for len(b) > 0 { for len(b) > 0 {
m, e := pread(fd, b, offset) m, e := pread(fd, b, offset)
if e == ERROR_EOF { if e == ERROR_EOF {
err = 0 err = nil
break break
} }
if e != 0 { if e != nil {
err = e err = e
break break
} }
@@ -311,18 +311,14 @@ on Windows, write_at changes the position of the file cursor, on *nix, it does n
will write to the location twice on *nix, and to two different locations on Windows will write to the location twice on *nix, and to two different locations on Windows
*/ */
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 { if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET return 0, .Invalid_Offset
} }
b, offset := data, offset b, offset := data, offset
for len(b) > 0 { for len(b) > 0 {
m, e := pwrite(fd, b, offset) m := pwrite(fd, b, offset) or_return
if e != 0 {
err = e
break
}
n += m n += m
b = b[m:] b = b[m:]
offset += i64(m) offset += i64(m)
@@ -338,6 +334,7 @@ stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)) stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle { get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h)) fd := win32.GetStdHandle(win32.DWORD(h))
return Handle(fd) return Handle(fd)
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
return attribs != win32.INVALID_FILE_ATTRIBUTES return attribs != win32.INVALID_FILE_ATTRIBUTES
} }
@(require_results)
is_file :: proc(path: string) -> bool { is_file :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
return false return false
} }
@(require_results)
is_dir :: proc(path: string) -> bool { is_dir :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -377,13 +376,14 @@ is_dir :: proc(path: string) -> bool {
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName // NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
@private cwd_lock := win32.SRWLOCK{} // zero is initialized @private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string { get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock) win32.AcquireSRWLockExclusive(&cwd_lock)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
sz_utf16 := win32.GetCurrentDirectoryW(0, nil) sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL. dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr)) sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL. assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
@@ -393,14 +393,14 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else "" return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wstr := win32.utf8_to_wstring(path, context.temp_allocator) wstr := win32.utf8_to_wstring(path, context.temp_allocator)
win32.AcquireSRWLockExclusive(&cwd_lock) win32.AcquireSRWLockExclusive(&cwd_lock)
if !win32.SetCurrentDirectoryW(wstr) { if !win32.SetCurrentDirectoryW(wstr) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
win32.ReleaseSRWLockExclusive(&cwd_lock) win32.ReleaseSRWLockExclusive(&cwd_lock)
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
} }
change_directory :: set_current_directory change_directory :: set_current_directory
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Mode is unused on Windows, but is needed on *nix // Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.CreateDirectoryW(wpath, nil) { if !win32.CreateDirectoryW(wpath, nil) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
remove_directory :: proc(path: string) -> (err: Errno) { remove_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.RemoveDirectoryW(wpath) { if !win32.RemoveDirectoryW(wpath) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
@(private) @(private, require_results)
is_abs :: proc(path: string) -> bool { is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' { if len(path) > 0 && path[0] == '/' {
return true return true
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
return false return false
} }
@(private) @(private, require_results)
fix_long_path :: proc(path: string) -> string { fix_long_path :: proc(path: string) -> string {
if len(path) < 248 { if len(path) < 248 {
return path return path
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
prefix :: `\\?` prefix :: `\\?`
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator) path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
copy(path_buf, prefix) copy(path_buf, prefix)
n := len(path) n := len(path)
r, w := 0, len(prefix) r, w := 0, len(prefix)
@@ -494,80 +494,69 @@ fix_long_path :: proc(path: string) -> string {
} }
link :: proc(old_name, new_name: string) -> (err: Errno) { link :: proc(old_name, new_name: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
n := win32.utf8_to_wstring(fix_long_path(new_name)) n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name)) o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil)) return Platform_Error(win32.CreateHardLinkW(n, o, nil))
} }
unlink :: proc(path: string) -> (err: Errno) { unlink :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.DeleteFileW(wpath) { if !win32.DeleteFileW(wpath) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
rename :: proc(old_path, new_path: string) -> (err: Errno) { rename :: proc(old_path, new_path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
from := win32.utf8_to_wstring(old_path, context.temp_allocator) from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator) to := win32.utf8_to_wstring(new_path, context.temp_allocator)
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
curr_off, e := seek(fd, 0, 1) curr_off := seek(fd, 0, 1) or_return
if e != 0 {
return e
}
defer seek(fd, curr_off, 0) defer seek(fd, curr_off, 0)
_, e = seek(fd, length, 0) _= seek(fd, length, 0) or_return
if e != 0 {
return e
}
ok := win32.SetEndOfFile(win32.HANDLE(fd)) ok := win32.SetEndOfFile(win32.HANDLE(fd))
if !ok { if !ok {
return Errno(win32.GetLastError()) return get_last_error()
} }
return ERROR_NONE return nil
} }
truncate :: proc(path: string, length: i64) -> (err: Errno) { truncate :: proc(path: string, length: i64) -> (err: Error) {
fd: Handle fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
if err != 0 {
return
}
defer close(fd) defer close(fd)
err = ftruncate(fd, length) return ftruncate(fd, length)
return
} }
remove :: proc(name: string) -> Errno { remove :: proc(name: string) -> Error {
p := win32.utf8_to_wstring(fix_long_path(name)) p := win32.utf8_to_wstring(fix_long_path(name))
err, err1: win32.DWORD err, err1: win32.DWORD
if !win32.DeleteFileW(p) { if !win32.DeleteFileW(p) {
err = win32.GetLastError() err = win32.GetLastError()
} }
if err == 0 { if err == 0 {
return 0 return nil
} }
if !win32.RemoveDirectoryW(p) { if !win32.RemoveDirectoryW(p) {
err1 = win32.GetLastError() err1 = win32.GetLastError()
} }
if err1 == 0 { if err1 == 0 {
return 0 return nil
} }
if err != err1 { if err != err1 {
@@ -588,16 +577,17 @@ remove :: proc(name: string) -> Errno {
} }
} }
return Errno(err) return Platform_Error(err)
} }
pipe :: proc() -> (r, w: Handle, err: Errno) { @(require_results)
pipe :: proc() -> (r, w: Handle, err: Error) {
sa: win32.SECURITY_ATTRIBUTES sa: win32.SECURITY_ATTRIBUTES
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
sa.bInheritHandle = true sa.bInheritHandle = true
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) { if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
+98 -66
View File
@@ -1,6 +1,8 @@
package os package os
import "base:intrinsics"
import "base:runtime" import "base:runtime"
import "core:io"
import "core:strconv" import "core:strconv"
import "core:unicode/utf8" import "core:unicode/utf8"
@@ -13,15 +15,15 @@ SEEK_SET :: 0
SEEK_CUR :: 1 SEEK_CUR :: 1
SEEK_END :: 2 SEEK_END :: 2
write_string :: proc(fd: Handle, str: string) -> (int, Errno) { write_string :: proc(fd: Handle, str: string) -> (int, Error) {
return write(fd, transmute([]byte)str) return write(fd, transmute([]byte)str)
} }
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) { write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
return write(fd, []byte{b}) return write(fd, []byte{b})
} }
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) { write_rune :: proc(fd: Handle, r: rune) -> (int, Error) {
if r < utf8.RUNE_SELF { if r < utf8.RUNE_SELF {
return write_byte(fd, byte(r)) return write_byte(fd, byte(r))
} }
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
return write(fd, b[:n]) return write(fd, b[:n])
} }
write_encoded_rune :: proc(fd: Handle, r: rune) { write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
write_byte(fd, '\'') wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
n^ += m
if merr != nil {
err^ = merr
return true
}
return false
}
if wrap(write_byte(f, '\''), &n, &err) { return }
switch r { switch r {
case '\a': write_string(fd, "\\a") case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
case '\b': write_string(fd, "\\b") case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
case '\e': write_string(fd, "\\e") case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
case '\f': write_string(fd, "\\f") case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
case '\n': write_string(fd, "\\n") case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
case '\r': write_string(fd, "\\r") case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
case '\t': write_string(fd, "\\t") case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
case '\v': write_string(fd, "\\v") case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
case: case:
if r < 32 { if r < 32 {
write_string(fd, "\\x") if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) { switch len(s) {
case 0: write_string(fd, "00") case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: write_rune(fd, '0') case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
case 2: write_string(fd, s) case 2: if wrap(write_string(f, s), &n, &err) { return }
} }
} else { } else {
write_rune(fd, r) if wrap(write_rune(f, r), &n, &err) { return }
} }
} }
write_byte(fd, '\'') _ = wrap(write_byte(f, '\''), &n, &err)
return
} }
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) { read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min { if len(buf) < min {
return 0, -1 return 0, io.Error.Short_Buffer
} }
nn := max(int) nn := max(int)
for nn > 0 && n < min && err == 0 { for nn > 0 && n < min && err == nil {
nn, err = read(fd, buf[n:]) nn, err = read(fd, buf[n:])
n += nn n += nn
} }
if n >= min { if n >= min {
err = 0 err = nil
} }
return return
} }
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) { read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Error) {
return read_at_least(fd, buf, len(buf)) return read_at_least(fd, buf, len(buf))
} }
@(require_results)
file_size_from_path :: proc(path: string) -> i64 { file_size_from_path :: proc(path: string) -> i64 {
fd, err := open(path, O_RDONLY, 0) fd, err := open(path, O_RDONLY, 0)
if err != 0 { if err != nil {
return -1 return -1
} }
defer close(fd) defer close(fd)
length: i64 length: i64
if length, err = file_size(fd); err != 0 { if length, err = file_size(fd); err != nil {
return -1 return -1
} }
return length return length
} }
@(require_results)
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator err: Error
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
fd, err := open(name, O_RDONLY, 0) success = err == nil
if err != 0 { return
return nil, false
}
defer close(fd)
return read_entire_file_from_handle(fd, allocator, loc)
} }
@(require_results)
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator err: Error
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
length: i64 success = err == nil
err: Errno return
if length, err = file_size(fd); err != 0 {
return nil, false
}
if length <= 0 {
return nil, true
}
data = make([]byte, int(length), allocator, loc)
if data == nil {
return nil, false
}
bytes_read, read_err := read_full(fd, data)
if read_err != ERROR_NONE {
delete(data)
return nil, false
}
return data[:bytes_read], true
} }
read_entire_file :: proc { read_entire_file :: proc {
@@ -136,7 +127,50 @@ read_entire_file :: proc {
read_entire_file_from_handle, read_entire_file_from_handle,
} }
@(require_results)
read_entire_file_from_filename_or_err :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
context.allocator = allocator
fd := open(name, O_RDONLY, 0) or_return
defer close(fd)
return read_entire_file_from_handle_or_err(fd, allocator, loc)
}
@(require_results)
read_entire_file_from_handle_or_err :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
context.allocator = allocator
length := file_size(fd) or_return
if length <= 0 {
return nil, nil
}
data = make([]byte, int(length), allocator, loc) or_return
if data == nil {
return nil, nil
}
defer if err != nil {
delete(data, allocator)
}
bytes_read := read_full(fd, data) or_return
data = data[:bytes_read]
return
}
read_entire_file_or_err :: proc {
read_entire_file_from_filename_or_err,
read_entire_file_from_handle_or_err,
}
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) { write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
return write_entire_file_or_err(name, data, truncate) == nil
}
@(require_results)
write_entire_file_or_err :: proc(name: string, data: []byte, truncate := true) -> Error {
flags: int = O_WRONLY|O_CREATE flags: int = O_WRONLY|O_CREATE
if truncate { if truncate {
flags |= O_TRUNC flags |= O_TRUNC
@@ -148,21 +182,18 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
} }
fd, err := open(name, flags, mode) fd := open(name, flags, mode) or_return
if err != 0 {
return false
}
defer close(fd) defer close(fd)
_, write_err := write(fd, data) _ = write(fd, data) or_return
return write_err == 0 return nil
} }
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
return write(fd, ([^]byte)(data)[:len]) return write(fd, ([^]byte)(data)[:len])
} }
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
return read(fd, ([^]byte)(data)[:len]) return read(fd, ([^]byte)(data)[:len])
} }
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
heap_resize :: runtime.heap_resize heap_resize :: runtime.heap_resize
heap_free :: runtime.heap_free heap_free :: runtime.heap_free
@(require_results)
processor_core_count :: proc() -> int { processor_core_count :: proc() -> int {
return _processor_core_count() return _processor_core_count()
} }
+27 -11
View File
@@ -10,20 +10,29 @@ file_allocator :: proc() -> runtime.Allocator {
temp_allocator_proc :: runtime.arena_allocator_proc temp_allocator_proc :: runtime.arena_allocator_proc
@(private="file")
MAX_TEMP_ARENA_COUNT :: 2
@(private="file", thread_local) @(private="file", thread_local)
global_default_temp_allocator_arena: runtime.Arena global_default_temp_allocator_arenas: [MAX_TEMP_ARENA_COUNT]runtime.Arena
@(private="file", thread_local)
global_default_temp_allocator_index: uint
@(require_results) @(require_results)
temp_allocator :: proc() -> runtime.Allocator { temp_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{ return runtime.Allocator{
procedure = temp_allocator_proc, procedure = temp_allocator_proc,
data = &global_default_temp_allocator_arena, data = &global_default_temp_allocator_arenas[global_default_temp_allocator_index],
} }
} }
@(require_results) @(require_results)
temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) { temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) {
temp = runtime.arena_temp_begin(&global_default_temp_allocator_arena, loc) temp = runtime.arena_temp_begin(&global_default_temp_allocator_arenas[global_default_temp_allocator_index], loc)
return return
} }
@@ -33,16 +42,23 @@ temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_locatio
@(fini, private) @(fini, private)
temp_allocator_fini :: proc() { temp_allocator_fini :: proc() {
runtime.arena_destroy(&global_default_temp_allocator_arena) for &arena in global_default_temp_allocator_arenas {
global_default_temp_allocator_arena = {} runtime.arena_destroy(&arena)
}
global_default_temp_allocator_arenas = {}
} }
@(deferred_out=temp_allocator_temp_end) TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) { runtime.arena_temp_end(temp, loc)
if ignore { if temp.arena != nil {
return {}, loc global_default_temp_allocator_index = (global_default_temp_allocator_index-1)%MAX_TEMP_ARENA_COUNT
} else {
return temp_allocator_temp_begin(loc), loc
} }
} }
@(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
return tmp, loc
}
+3 -1
View File
@@ -3,6 +3,8 @@ package os2
import "base:runtime" import "base:runtime"
import "core:slice" import "core:slice"
read_dir :: read_directory
@(require_results) @(require_results)
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) { read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
if f == nil { if f == nil {
@@ -57,6 +59,7 @@ read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -
} }
Read_Directory_Iterator :: struct { Read_Directory_Iterator :: struct {
f: ^File, f: ^File,
impl: Read_Directory_Iterator_Impl, impl: Read_Directory_Iterator_Impl,
@@ -72,7 +75,6 @@ read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
_read_directory_iterator_destroy(it) _read_directory_iterator_destroy(it)
} }
// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone` // NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
@(require_results) @(require_results)
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+14 -1
View File
@@ -23,10 +23,21 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
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, nil, d.dwReserved0)
// fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) 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.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)) 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 return
} }
@@ -46,6 +57,8 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
return return
} }
TEMP_ALLOCATOR_GUARD()
for !it.impl.no_more_files { for !it.impl.no_more_files {
err: Error err: Error
file_info_delete(it.impl.prev_fi, file_allocator()) file_info_delete(it.impl.prev_fi, file_allocator())
+2 -1
View File
@@ -42,13 +42,14 @@ Error :: union #shared_nil {
ERROR_NONE :: Error{} ERROR_NONE :: Error{}
@(require_results)
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {} v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0 return i32(v), i32(v) != 0
} }
@(require_results)
error_string :: proc(ferr: Error) -> string { error_string :: proc(ferr: Error) -> string {
if ferr == nil { if ferr == nil {
return "" return ""
+2 -2
View File
@@ -4,8 +4,8 @@ package os2
import "core:sys/linux" import "core:sys/linux"
@(rodata) @(rodata)
_errno_strings : [linux.Errno]string = { _errno_strings := [linux.Errno]string{
.NONE = "Success", .NONE = "",
.EPERM = "Operation not permitted", .EPERM = "Operation not permitted",
.ENOENT = "No such file or directory", .ENOENT = "No such file or directory",
.ESRCH = "No such process", .ESRCH = "No such process",
+9
View File
@@ -104,6 +104,15 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
return _open(name, flags, perm) return _open(name, flags, perm)
} }
// @(require_results)
// open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (^File, Error) {
// if buffer_size == 0 {
// return _open(name, flags, perm)
// }
// return _open_buffered(name, buffer_size, flags, perm)
// }
@(require_results) @(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File { new_file :: proc(handle: uintptr, name: string) -> ^File {
return _new_file(handle, name) or_else panic("Out of memory") return _new_file(handle, name) or_else panic("Out of memory")
+72 -10
View File
@@ -1,9 +1,10 @@
//+private //+private
package os2 package os2
import "base:runtime"
import "core:io" import "core:io"
import "core:time" import "core:time"
import "base:runtime" import "core:sync"
import "core:sys/linux" import "core:sys/linux"
File_Impl :: struct { File_Impl :: struct {
@@ -11,6 +12,10 @@ File_Impl :: struct {
name: string, name: string,
fd: linux.Fd, fd: linux.Fd,
allocator: runtime.Allocator, allocator: runtime.Allocator,
buffer: []byte,
rw_mutex: sync.RW_Mutex, // read write calls
p_mutex: sync.Mutex, // pread pwrite calls
} }
_stdin := File{ _stdin := File{
@@ -49,9 +54,9 @@ _standard_stream_init :: proc() {
fd = 2, fd = 2,
} }
stdin_impl.allocator = _file_allocator() stdin_impl.allocator = file_allocator()
stdout_impl.allocator = _file_allocator() stdout_impl.allocator = file_allocator()
stderr_impl.allocator = _file_allocator() stderr_impl.allocator = file_allocator()
_stdin.impl = &stdin_impl _stdin.impl = &stdin_impl
_stdout.impl = &stdout_impl _stdout.impl = &stdout_impl
@@ -67,10 +72,6 @@ _standard_stream_init :: proc() {
stderr = &_stderr stderr = &_stderr
} }
_file_allocator :: proc() -> runtime.Allocator {
return heap_allocator()
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) { _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return name_cstr := temp_cstring(name) or_return
@@ -116,13 +117,30 @@ _new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
return &impl.file, nil return &impl.file, nil
} }
@(require_results)
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
assert(buffer_size > 0)
f, err = _open(name, flags, perm)
if f != nil && err == nil {
impl := (^File_Impl)(f.impl)
impl.buffer = make([]byte, buffer_size, file_allocator())
f.stream.procedure = _file_stream_buffered_proc
}
return
}
_destroy :: proc(f: ^File_Impl) -> Error { _destroy :: proc(f: ^File_Impl) -> Error {
if f == nil { if f == nil {
return nil return nil
} }
a := f.allocator a := f.allocator
delete(f.name, a) err0 := delete(f.name, a)
free(f, a) err1 := delete(f.buffer, a)
err2 := free(f, a)
err0 or_return
err1 or_return
err2 or_return
return nil return nil
} }
@@ -470,3 +488,47 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
return 0, .Empty return 0, .Empty
} }
@(private="package")
_file_stream_buffered_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
f := (^File_Impl)(stream_data)
ferr: Error
switch mode {
case .Read:
n, ferr = _read(f, p)
err = error_to_io_error(ferr)
return
case .Read_At:
n, ferr = _read_at(f, p, offset)
err = error_to_io_error(ferr)
return
case .Write:
n, ferr = _write(f, p)
err = error_to_io_error(ferr)
return
case .Write_At:
n, ferr = _write_at(f, p, offset)
err = error_to_io_error(ferr)
return
case .Seek:
n, ferr = _seek(f, offset, whence)
err = error_to_io_error(ferr)
return
case .Size:
n, ferr = _file_size(f)
err = error_to_io_error(ferr)
return
case .Flush:
ferr = _flush(f)
err = error_to_io_error(ferr)
return
case .Close, .Destroy:
ferr = _close(f)
err = error_to_io_error(ferr)
return
case .Query:
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
}
return 0, .Empty
}
+20 -5
View File
@@ -73,6 +73,24 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
return return
} }
read_at_least :: proc(f: ^File, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer
}
nn := max(int)
for nn > 0 && n < min && err == nil {
nn, err = read(f, buf[n:])
n += nn
}
if n >= min {
err = nil
}
return
}
read_full :: proc(f: ^File, buf: []byte) -> (n: int, err: Error) {
return read_at_least(f, buf, len(buf))
}
write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
return write(f, ([^]byte)(data)[:len]) return write(f, ([^]byte)(data)[:len])
@@ -155,11 +173,8 @@ write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := tru
if truncate { if truncate {
flags |= O_TRUNC flags |= O_TRUNC
} }
f, err := open(name, flags, perm) f := open(name, flags, perm) or_return
if err != nil { _, err := write(f, data)
return err
}
_, err = write(f, data)
if cerr := close(f); cerr != nil && err == nil { if cerr := close(f); cerr != nil && err == nil {
err = cerr err = cerr
} }
+57 -9
View File
@@ -33,6 +33,11 @@ File_Impl :: struct {
allocator: runtime.Allocator, allocator: runtime.Allocator,
r_buf: []byte,
w_buf: []byte,
w_n: int,
max_consecutive_empty_writes: int,
rw_mutex: sync.RW_Mutex, // read write calls rw_mutex: sync.RW_Mutex, // read write calls
p_mutex: sync.Mutex, // pread pwrite calls p_mutex: sync.Mutex, // pread pwrite calls
} }
@@ -60,8 +65,9 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
err = .Not_Exist err = .Not_Exist
return return
} }
TEMP_ALLOCATOR_GUARD()
path := _fix_long_path(name) or_return path := _fix_long_path(name, temp_allocator()) or_return
access: u32 access: u32
switch flags & {.Read, .Write} { switch flags & {.Read, .Write} {
case {.Read}: access = win32.FILE_GENERIC_READ case {.Read}: access = win32.FILE_GENERIC_READ
@@ -164,6 +170,26 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return &impl.file, nil return &impl.file, nil
} }
@(require_results)
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
assert(buffer_size > 0)
flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return
return _new_file_buffered(handle, name, buffer_size)
}
_new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
f, err = _new_file(handle, name)
if f != nil && err == nil {
impl := (^File_Impl)(f.impl)
impl.r_buf = make([]byte, buffer_size, file_allocator())
impl.w_buf = make([]byte, buffer_size, file_allocator())
}
return
}
_fd :: proc(f: ^File) -> uintptr { _fd :: proc(f: ^File) -> uintptr {
if f == nil || f.impl == nil { if f == nil || f.impl == nil {
return INVALID_HANDLE return INVALID_HANDLE
@@ -180,9 +206,13 @@ _destroy :: proc(f: ^File_Impl) -> Error {
err0 := free(f.wname, a) err0 := free(f.wname, a)
err1 := delete(f.name, a) err1 := delete(f.name, a)
err2 := free(f, a) err2 := free(f, a)
err3 := delete(f.r_buf, a)
err4 := delete(f.w_buf, a)
err0 or_return err0 or_return
err1 or_return err1 or_return
err2 or_return err2 or_return
err3 or_return
err4 or_return
return nil return nil
} }
@@ -230,6 +260,10 @@ _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, er
} }
_read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) { _read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
return _read_internal(f, p)
}
_read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) { read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
@@ -351,6 +385,9 @@ _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error)
} }
_write :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) { _write :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
return _write_internal(f, p)
}
_write_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
if len(p) == 0 { if len(p) == 0 {
return return
} }
@@ -435,6 +472,9 @@ _sync :: proc(f: ^File) -> Error {
} }
_flush :: proc(f: ^File_Impl) -> Error { _flush :: proc(f: ^File_Impl) -> Error {
return _flush(f)
}
_flush_internal :: proc(f: ^File_Impl) -> Error {
handle := _handle(&f.file) handle := _handle(&f.file)
if !win32.FlushFileBuffers(handle) { if !win32.FlushFileBuffers(handle) {
return _get_platform_error() return _get_platform_error()
@@ -457,7 +497,8 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
} }
_remove :: proc(name: string) -> Error { _remove :: proc(name: string) -> Error {
p := _fix_long_path(name) or_return TEMP_ALLOCATOR_GUARD()
p := _fix_long_path(name, temp_allocator()) or_return
err, err1: Error err, err1: Error
if !win32.DeleteFileW(p) { if !win32.DeleteFileW(p) {
err = _get_platform_error() err = _get_platform_error()
@@ -494,8 +535,9 @@ _remove :: proc(name: string) -> Error {
} }
_rename :: proc(old_path, new_path: string) -> Error { _rename :: proc(old_path, new_path: string) -> Error {
from := _fix_long_path(old_path) or_return TEMP_ALLOCATOR_GUARD()
to := _fix_long_path(new_path) or_return from := _fix_long_path(old_path, temp_allocator()) or_return
to := _fix_long_path(new_path, temp_allocator()) or_return
if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
return nil return nil
} }
@@ -504,8 +546,9 @@ _rename :: proc(old_path, new_path: string) -> Error {
} }
_link :: proc(old_name, new_name: string) -> Error { _link :: proc(old_name, new_name: string) -> Error {
o := _fix_long_path(old_name) or_return TEMP_ALLOCATOR_GUARD()
n := _fix_long_path(new_name) or_return o := _fix_long_path(old_name, temp_allocator()) or_return
n := _fix_long_path(new_name, temp_allocator()) or_return
if win32.CreateHardLinkW(n, o, nil) { if win32.CreateHardLinkW(n, o, nil) {
return nil return nil
} }
@@ -592,7 +635,9 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er
@thread_local @thread_local
rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
p := _fix_long_path(name) or_return TEMP_ALLOCATOR_GUARD()
p := _fix_long_path(name, temp_allocator()) or_return
handle := _open_sym_link(p) or_return handle := _open_sym_link(p) or_return
defer win32.CloseHandle(handle) defer win32.CloseHandle(handle)
@@ -667,7 +712,8 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error {
} }
_chdir :: proc(name: string) -> Error { _chdir :: proc(name: string) -> Error {
p := _fix_long_path(name) or_return TEMP_ALLOCATOR_GUARD()
p := _fix_long_path(name, temp_allocator()) or_return
if !win32.SetCurrentDirectoryW(p) { if !win32.SetCurrentDirectoryW(p) {
return _get_platform_error() return _get_platform_error()
} }
@@ -723,7 +769,8 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
} }
_exists :: proc(path: string) -> bool { _exists :: proc(path: string) -> bool {
wpath, _ := _fix_long_path(path) TEMP_ALLOCATOR_GUARD()
wpath, _ := _fix_long_path(path, temp_allocator())
attribs := win32.GetFileAttributesW(wpath) attribs := win32.GetFileAttributesW(wpath)
return attribs != win32.INVALID_FILE_ATTRIBUTES return attribs != win32.INVALID_FILE_ATTRIBUTES
} }
@@ -773,6 +820,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
@(private="package", require_results) @(private="package", require_results)
win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: [^]u16, err: runtime.Allocator_Error) { win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: [^]u16, err: runtime.Allocator_Error) {
ws = raw_data(win32_utf8_to_utf16(s, allocator) or_return) ws = raw_data(win32_utf8_to_utf16(s, allocator) or_return)
+6 -5
View File
@@ -13,7 +13,8 @@ _is_path_separator :: proc(c: byte) -> bool {
} }
_mkdir :: proc(name: string, perm: int) -> Error { _mkdir :: proc(name: string, perm: int) -> Error {
if !win32.CreateDirectoryW(_fix_long_path(name) or_return, nil) { TEMP_ALLOCATOR_GUARD()
if !win32.CreateDirectoryW(_fix_long_path(name, temp_allocator()) or_return, nil) {
return _get_platform_error() return _get_platform_error()
} }
return nil return nil
@@ -169,13 +170,13 @@ init_long_path_support :: proc() {
} }
@(require_results) @(require_results)
_fix_long_path_slice :: proc(path: string) -> ([]u16, runtime.Allocator_Error) { _fix_long_path_slice :: proc(path: string, allocator: runtime.Allocator) -> ([]u16, runtime.Allocator_Error) {
return win32_utf8_to_utf16(_fix_long_path_internal(path), temp_allocator()) return win32_utf8_to_utf16(_fix_long_path_internal(path), allocator)
} }
@(require_results) @(require_results)
_fix_long_path :: proc(path: string) -> (win32.wstring, runtime.Allocator_Error) { _fix_long_path :: proc(path: string, allocator: runtime.Allocator) -> (win32.wstring, runtime.Allocator_Error) {
return win32_utf8_to_wstring(_fix_long_path_internal(path), temp_allocator()) return win32_utf8_to_wstring(_fix_long_path_internal(path), allocator)
} }
@(require_results) @(require_results)
+2 -2
View File
@@ -12,8 +12,8 @@ File_Info :: struct {
name: string, name: string,
inode: u128, // might be zero if cannot be determined inode: u128, // might be zero if cannot be determined
size: i64, size: i64 `fmt:"M"`,
mode: int, mode: int `fmt:"o"`,
type: File_Type, type: File_Type,
creation_time: time.Time, creation_time: time.Time,
+7 -9
View File
@@ -6,25 +6,22 @@ import "core:time"
import "core:strings" import "core:strings"
import win32 "core:sys/windows" import win32 "core:sys/windows"
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if f == nil || (^File_Impl)(f.impl).fd == nil { if f == nil || (^File_Impl)(f.impl).fd == nil {
return {}, nil return
} }
path, err := _cleanpath_from_handle(f, allocator) path := _cleanpath_from_handle(f, allocator) or_return
if err != nil {
return {}, err
}
h := _handle(f) h := _handle(f)
switch win32.GetFileType(h) { switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi := File_Info { fi = File_Info {
fullpath = path, fullpath = path,
name = basename(path), name = basename(path),
type = file_type(h), type = file_type(h),
} }
return fi, nil return
} }
return _file_info_from_get_file_information_by_handle(path, h, allocator) return _file_info_from_get_file_information_by_handle(path, h, allocator)
@@ -67,8 +64,9 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
if len(name) == 0 { if len(name) == 0 {
return {}, .Not_Exist return {}, .Not_Exist
} }
TEMP_ALLOCATOR_GUARD()
wname := _fix_long_path(name) or_return wname := _fix_long_path(name, temp_allocator()) or_return
fa: win32.WIN32_FILE_ATTRIBUTE_DATA fa: win32.WIN32_FILE_ATTRIBUTE_DATA
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
+7 -4
View File
@@ -3,8 +3,11 @@ package os2
import "base:runtime" import "base:runtime"
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) { TEMP_ALLOCATOR_GUARD()
//TODO tmpdir := get_env("TMPDIR", temp_allocator())
return "", nil if tmpdir == "" {
tmpdir = "/tmp"
}
return clone_string(tmpdir, allocator)
} }
+423 -268
View File
File diff suppressed because it is too large Load Diff
+29 -24
View File
@@ -2,54 +2,59 @@ package os
import "core:sys/es" import "core:sys/es"
Handle :: distinct int; Handle :: distinct int
Errno :: distinct int; _Platform_Error :: enum i32 {NONE}
ERROR_NONE :: (Errno) (es.SUCCESS); // ERROR_NONE :: Error(es.SUCCESS)
O_RDONLY :: 0x1; O_RDONLY :: 0x1
O_WRONLY :: 0x2; O_WRONLY :: 0x2
O_CREATE :: 0x4; O_CREATE :: 0x4
O_TRUNC :: 0x8; O_TRUNC :: 0x8
stderr : Handle = 0; stderr : Handle = 0
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return (int) (es.ThreadGetID(es.CURRENT_THREAD)); return (int) (es.ThreadGetID(es.CURRENT_THREAD))
} }
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return es.HeapAllocate(size, zero_memory); return es.HeapAllocate(size, zero_memory)
} }
heap_free :: proc(ptr: rawptr) { heap_free :: proc(ptr: rawptr) {
es.HeapFree(ptr); es.HeapFree(ptr)
} }
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
return es.HeapReallocate(ptr, new_size, false); return es.HeapReallocate(ptr, new_size, false)
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
return (Handle) (0), (Errno) (1); return (Handle) (0), (Error) (1)
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
return (Errno) (1); return (Error) (1)
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { file_size :: proc(fd: Handle) -> (i64, Error) {
return (i64) (0), (Errno) (1); return (i64) (0), (Error) (1)
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
return (int) (0), (Errno) (1); return (int) (0), (Error) (1)
} }
write :: proc(fd: Handle, data: []u8) -> (int, Errno) { write :: proc(fd: Handle, data: []u8) -> (int, Error) {
return (int) (0), (Errno) (1); return (int) (0), (Error) (1)
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
return (i64) (0), (Errno) (1); return (i64) (0), (Error) (1)
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
+334 -196
View File
@@ -9,105 +9,200 @@ import "core:c"
Handle :: distinct i32 Handle :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 _Platform_Error :: enum i32 {
EPERM: Errno : 1 NONE = 0,
ENOENT: Errno : 2 EPERM = 1,
ESRCH: Errno : 3 ENOENT = 2,
EINTR: Errno : 4 ESRCH = 3,
EIO: Errno : 5 EINTR = 4,
ENXIO: Errno : 6 EIO = 5,
E2BIG: Errno : 7 ENXIO = 6,
ENOEXEC: Errno : 8 E2BIG = 7,
EBADF: Errno : 9 ENOEXEC = 8,
ECHILD: Errno : 10 EBADF = 9,
EBEADLK: Errno : 11 ECHILD = 10,
ENOMEM: Errno : 12 EBEADLK = 11,
EACCESS: Errno : 13 ENOMEM = 12,
EFAULT: Errno : 14 EACCESS = 13,
ENOTBLK: Errno : 15 EFAULT = 14,
EBUSY: Errno : 16 ENOTBLK = 15,
EEXIST: Errno : 17 EBUSY = 16,
EXDEV: Errno : 18 EEXIST = 17,
ENODEV: Errno : 19 EXDEV = 18,
ENOTDIR: Errno : 20 ENODEV = 19,
EISDIR: Errno : 21 ENOTDIR = 20,
EINVAL: Errno : 22 EISDIR = 21,
ENFILE: Errno : 23 EINVAL = 22,
EMFILE: Errno : 24 ENFILE = 23,
ENOTTY: Errno : 25 EMFILE = 24,
ETXTBSY: Errno : 26 ENOTTY = 25,
EFBIG: Errno : 27 ETXTBSY = 26,
ENOSPC: Errno : 28 EFBIG = 27,
ESPIPE: Errno : 29 ENOSPC = 28,
EROFS: Errno : 30 ESPIPE = 29,
EMLINK: Errno : 31 EROFS = 30,
EPIPE: Errno : 32 EMLINK = 31,
EDOM: Errno : 33 EPIPE = 32,
ERANGE: Errno : 34 /* Result too large */ EDOM = 33,
EAGAIN: Errno : 35 ERANGE = 34, /* Result too large */
EINPROGRESS: Errno : 36 EAGAIN = 35,
EALREADY: Errno : 37 EINPROGRESS = 36,
ENOTSOCK: Errno : 38 EALREADY = 37,
EDESTADDRREQ: Errno : 39 ENOTSOCK = 38,
EMSGSIZE: Errno : 40 EDESTADDRREQ = 39,
EPROTOTYPE: Errno : 41 EMSGSIZE = 40,
ENOPROTOOPT: Errno : 42 EPROTOTYPE = 41,
EPROTONOSUPPORT: Errno : 43 ENOPROTOOPT = 42,
ESOCKTNOSUPPORT: Errno : 44 EPROTONOSUPPORT = 43,
EOPNOTSUPP: Errno : 45 ESOCKTNOSUPPORT = 44,
EPFNOSUPPORT: Errno : 46 EOPNOTSUPP = 45,
EAFNOSUPPORT: Errno : 47 EPFNOSUPPORT = 46,
EADDRINUSE: Errno : 48 EAFNOSUPPORT = 47,
EADDRNOTAVAIL: Errno : 49 EADDRINUSE = 48,
ENETDOWN: Errno : 50 EADDRNOTAVAIL = 49,
ENETUNREACH: Errno : 51 ENETDOWN = 50,
ENETRESET: Errno : 52 ENETUNREACH = 51,
ECONNABORTED: Errno : 53 ENETRESET = 52,
ECONNRESET: Errno : 54 ECONNABORTED = 53,
ENOBUFS: Errno : 55 ECONNRESET = 54,
EISCONN: Errno : 56 ENOBUFS = 55,
ENOTCONN: Errno : 57 EISCONN = 56,
ESHUTDOWN: Errno : 58 ENOTCONN = 57,
ETIMEDOUT: Errno : 60 ESHUTDOWN = 58,
ECONNREFUSED: Errno : 61 ETIMEDOUT = 60,
ELOOP: Errno : 62 ECONNREFUSED = 61,
ENAMETOOLING: Errno : 63 ELOOP = 62,
EHOSTDOWN: Errno : 64 ENAMETOOLING = 63,
EHOSTUNREACH: Errno : 65 EHOSTDOWN = 64,
ENOTEMPTY: Errno : 66 EHOSTUNREACH = 65,
EPROCLIM: Errno : 67 ENOTEMPTY = 66,
EUSERS: Errno : 68 EPROCLIM = 67,
EDQUOT: Errno : 69 EUSERS = 68,
ESTALE: Errno : 70 EDQUOT = 69,
EBADRPC: Errno : 72 ESTALE = 70,
ERPCMISMATCH: Errno : 73 EBADRPC = 72,
EPROGUNAVAIL: Errno : 74 ERPCMISMATCH = 73,
EPROGMISMATCH: Errno : 75 EPROGUNAVAIL = 74,
EPROCUNAVAIL: Errno : 76 EPROGMISMATCH = 75,
ENOLCK: Errno : 77 EPROCUNAVAIL = 76,
ENOSYS: Errno : 78 ENOLCK = 77,
EFTYPE: Errno : 79 ENOSYS = 78,
EAUTH: Errno : 80 EFTYPE = 79,
ENEEDAUTH: Errno : 81 EAUTH = 80,
EIDRM: Errno : 82 ENEEDAUTH = 81,
ENOMSG: Errno : 83 EIDRM = 82,
EOVERFLOW: Errno : 84 ENOMSG = 83,
ECANCELED: Errno : 85 EOVERFLOW = 84,
EILSEQ: Errno : 86 ECANCELED = 85,
ENOATTR: Errno : 87 EILSEQ = 86,
EDOOFUS: Errno : 88 ENOATTR = 87,
EBADMSG: Errno : 89 EDOOFUS = 88,
EMULTIHOP: Errno : 90 EBADMSG = 89,
ENOLINK: Errno : 91 EMULTIHOP = 90,
EPROTO: Errno : 92 ENOLINK = 91,
ENOTCAPABLE: Errno : 93 EPROTO = 92,
ECAPMODE: Errno : 94 ENOTCAPABLE = 93,
ENOTRECOVERABLE: Errno : 95 ECAPMODE = 94,
EOWNERDEAD: Errno : 96 ENOTRECOVERABLE = 95,
EOWNERDEAD = 96,
}
EPERM :: Platform_Error.EPERM
ENOENT :: Platform_Error.ENOENT
ESRCH :: Platform_Error.ESRCH
EINTR :: Platform_Error.EINTR
EIO :: Platform_Error.EIO
ENXIO :: Platform_Error.ENXIO
E2BIG :: Platform_Error.E2BIG
ENOEXEC :: Platform_Error.ENOEXEC
EBADF :: Platform_Error.EBADF
ECHILD :: Platform_Error.ECHILD
EBEADLK :: Platform_Error.EBEADLK
ENOMEM :: Platform_Error.ENOMEM
EACCESS :: Platform_Error.EACCESS
EFAULT :: Platform_Error.EFAULT
ENOTBLK :: Platform_Error.ENOTBLK
EBUSY :: Platform_Error.EBUSY
EEXIST :: Platform_Error.EEXIST
EXDEV :: Platform_Error.EXDEV
ENODEV :: Platform_Error.ENODEV
ENOTDIR :: Platform_Error.ENOTDIR
EISDIR :: Platform_Error.EISDIR
EINVAL :: Platform_Error.EINVAL
ENFILE :: Platform_Error.ENFILE
EMFILE :: Platform_Error.EMFILE
ENOTTY :: Platform_Error.ENOTTY
ETXTBSY :: Platform_Error.ETXTBSY
EFBIG :: Platform_Error.EFBIG
ENOSPC :: Platform_Error.ENOSPC
ESPIPE :: Platform_Error.ESPIPE
EROFS :: Platform_Error.EROFS
EMLINK :: Platform_Error.EMLINK
EPIPE :: Platform_Error.EPIPE
EDOM :: Platform_Error.EDOM
ERANGE :: Platform_Error.ERANGE
EAGAIN :: Platform_Error.EAGAIN
EINPROGRESS :: Platform_Error.EINPROGRESS
EALREADY :: Platform_Error.EALREADY
ENOTSOCK :: Platform_Error.ENOTSOCK
EDESTADDRREQ :: Platform_Error.EDESTADDRREQ
EMSGSIZE :: Platform_Error.EMSGSIZE
EPROTOTYPE :: Platform_Error.EPROTOTYPE
ENOPROTOOPT :: Platform_Error.ENOPROTOOPT
EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT
ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT
EOPNOTSUPP :: Platform_Error.EOPNOTSUPP
EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT
EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT
EADDRINUSE :: Platform_Error.EADDRINUSE
EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL
ENETDOWN :: Platform_Error.ENETDOWN
ENETUNREACH :: Platform_Error.ENETUNREACH
ENETRESET :: Platform_Error.ENETRESET
ECONNABORTED :: Platform_Error.ECONNABORTED
ECONNRESET :: Platform_Error.ECONNRESET
ENOBUFS :: Platform_Error.ENOBUFS
EISCONN :: Platform_Error.EISCONN
ENOTCONN :: Platform_Error.ENOTCONN
ESHUTDOWN :: Platform_Error.ESHUTDOWN
ETIMEDOUT :: Platform_Error.ETIMEDOUT
ECONNREFUSED :: Platform_Error.ECONNREFUSED
ELOOP :: Platform_Error.ELOOP
ENAMETOOLING :: Platform_Error.ENAMETOOLING
EHOSTDOWN :: Platform_Error.EHOSTDOWN
EHOSTUNREACH :: Platform_Error.EHOSTUNREACH
ENOTEMPTY :: Platform_Error.ENOTEMPTY
EPROCLIM :: Platform_Error.EPROCLIM
EUSERS :: Platform_Error.EUSERS
EDQUOT :: Platform_Error.EDQUOT
ESTALE :: Platform_Error.ESTALE
EBADRPC :: Platform_Error.EBADRPC
ERPCMISMATCH :: Platform_Error.ERPCMISMATCH
EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL
EPROGMISMATCH :: Platform_Error.EPROGMISMATCH
EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL
ENOLCK :: Platform_Error.ENOLCK
ENOSYS :: Platform_Error.ENOSYS
EFTYPE :: Platform_Error.EFTYPE
EAUTH :: Platform_Error.EAUTH
ENEEDAUTH :: Platform_Error.ENEEDAUTH
EIDRM :: Platform_Error.EIDRM
ENOMSG :: Platform_Error.ENOMSG
EOVERFLOW :: Platform_Error.EOVERFLOW
ECANCELED :: Platform_Error.ECANCELED
EILSEQ :: Platform_Error.EILSEQ
ENOATTR :: Platform_Error.ENOATTR
EDOOFUS :: Platform_Error.EDOOFUS
EBADMSG :: Platform_Error.EBADMSG
EMULTIHOP :: Platform_Error.EMULTIHOP
ENOLINK :: Platform_Error.ENOLINK
EPROTO :: Platform_Error.EPROTO
ENOTCAPABLE :: Platform_Error.ENOTCAPABLE
ECAPMODE :: Platform_Error.ECAPMODE
ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE
EOWNERDEAD :: Platform_Error.EOWNERDEAD
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
@@ -258,13 +353,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -274,7 +369,7 @@ R_OK :: 4 // Test for read permission
F_KINFO :: 22 F_KINFO :: 22
foreign libc { foreign libc {
@(link_name="__error") __errno_location :: proc() -> ^c.int --- @(link_name="__error") __Error_location :: proc() -> ^c.int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
@@ -320,30 +415,38 @@ foreign dl {
@(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int --- @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---
} }
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__errno_location()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__Error_location()^)
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// If you read or write more than `INT_MAX` bytes, FreeBSD returns `EINVAL`. // If you read or write more than `INT_MAX` bytes, FreeBSD returns `EINVAL`.
@@ -354,124 +457,148 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
s, err := _fstat(fd) file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
if err != ERROR_NONE { size = -1
return -1, err s := _fstat(fd) or_return
} size = s.size
return s.size, ERROR_NONE return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr) res := _unix_rename(old_path_cstr, new_path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr) res := _unix_unlink(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode) res := _unix_mkdir(path_cstr, mode)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr) res := _unix_rmdir(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -490,38 +617,40 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {}
*/ */
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return 0, err return 0, err
} }
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
s, err := _stat(name) s, err := _stat(name)
if err != ERROR_NONE { if err != nil {
return 0, err return 0, err
} }
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_lstat(cstr, &s) result := _unix_lstat(cstr, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -529,54 +658,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_fstat(fd, &s) result := _unix_fstat(fd, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -586,8 +714,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -598,20 +726,21 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
return "", Errno{} return "", Error{}
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
// NOTE(Feoramund): The situation isn't ideal, but this was the best way I // NOTE(Feoramund): The situation isn't ideal, but this was the best way I
// could find to implement this. There are a couple outstanding bug reports // could find to implement this. There are a couple outstanding bug reports
// regarding the desire to retrieve an absolute path from a handle, but to // regarding the desire to retrieve an absolute path from a handle, but to
@@ -626,14 +755,15 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo) res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo)
if res == -1 { if res == -1 {
return "", Errno(get_last_error()) return "", get_last_error()
} }
path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path)) path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path))
return path, ERROR_NONE return path, nil
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -644,27 +774,28 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask)) result := _unix_access(cstr, c.int(mask))
if result == -1 { if result == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
@@ -676,11 +807,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems. // an authoritative value for it across all systems.
@@ -692,7 +825,7 @@ get_current_directory :: proc() -> string {
if cwd != nil { if cwd != nil {
return string(cwd) return string(cwd)
} }
if Errno(get_last_error()) != ERANGE { if get_last_error() != ERANGE {
delete(buf) delete(buf)
return "" return ""
} }
@@ -701,14 +834,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr) res := _unix_chdir(cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -716,16 +849,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return cast(int) pthread_getthreadid_np() return cast(int) pthread_getthreadid_np()
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags)) handle := _unix_dlopen(cstr, c.int(flags))
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -741,6 +877,7 @@ dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -753,7 +890,7 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
count : int = 0 count : int = 0
count_size := size_of(count) count_size := size_of(count)
@@ -767,6 +904,7 @@ _processor_core_count :: proc() -> int {
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
+101 -69
View File
@@ -10,16 +10,14 @@ import "core:sys/haiku"
Handle :: i32 Handle :: i32
Pid :: i32 Pid :: i32
File_Time :: i64 File_Time :: i64
Errno :: i32 _Platform_Error :: haiku.Errno
MAX_PATH :: haiku.PATH_MAX MAX_PATH :: haiku.PATH_MAX
ENOSYS :: int(haiku.Errno.POSIX_ERROR_BASE) + 9 ENOSYS :: _Platform_Error(i32(haiku.Errno.POSIX_ERROR_BASE) + 9)
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno: 0
stdin: Handle = 0 stdin: Handle = 0
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
@@ -121,7 +119,7 @@ S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK
foreign libc { foreign libc {
@(link_name="_errnop") __error :: proc() -> ^c.int --- @(link_name="_errorp") __error :: proc() -> ^c.int ---
@(link_name="fork") _unix_fork :: proc() -> pid_t --- @(link_name="fork") _unix_fork :: proc() -> pid_t ---
@(link_name="getthrid") _unix_getthrid :: proc() -> int --- @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
@@ -179,38 +177,47 @@ Dirent :: struct {
Dir :: distinct rawptr // DIR* Dir :: distinct rawptr // DIR*
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__error()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__error()^)
} }
fork :: proc() -> (Pid, Errno) { @(require_results)
fork :: proc() -> (Pid, Error) {
pid := _unix_fork() pid := _unix_fork()
if pid == -1 { if pid == -1 {
return Pid(-1), Errno(get_last_error()) return Pid(-1), get_last_error()
} }
return Pid(pid), ERROR_NONE return Pid(pid), nil
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// In practice a read/write call would probably never read/write these big buffers all at once, // In practice a read/write call would probably never read/write these big buffers all at once,
@@ -220,47 +227,69 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return -1, err return -1, err
} }
return s.size, ERROR_NONE return s.size, nil
} }
// "Argv" arguments converted to Odin strings // "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
@@ -269,8 +298,8 @@ _alloc_command_line_arguments :: proc() -> []string {
return res return res
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -278,13 +307,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_stat(cstr, &s) res := _unix_stat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -292,55 +321,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized // deliberately uninitialized
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_fstat(fd, &s) res := _unix_fstat(fd, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -350,8 +378,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -361,22 +389,24 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
return "", Errno(ENOSYS) absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
return "", Error(ENOSYS)
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -387,26 +417,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path_cstr := transmute(cstring)path_ptr path_cstr := cstring(path_ptr)
path = strings.clone( string(path_cstr) ) path = strings.clone(string(path_cstr))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask)) res := _unix_access(cstr, c.int(mask))
if res == -1 { if res == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator) path_str := strings.clone_to_cstring(key, context.temp_allocator)
@@ -417,12 +448,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
info: haiku.system_info info: haiku.system_info
haiku.get_system_info(&info) haiku.get_system_info(&info)
+105 -60
View File
@@ -3,42 +3,45 @@ package os
import "base:runtime" import "base:runtime"
@(require_results)
is_path_separator :: proc(c: byte) -> bool { is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\' return c == '/' || c == '\\'
} }
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
flush :: proc(fd: Handle) -> (err: Errno) { flush :: proc(fd: Handle) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private="file") @(private="file")
read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Errno) { read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@@ -47,38 +50,42 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
MAX_RW :: 1<<30 MAX_RW :: 1<<30
@(private) @(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle { get_std_handle :: proc "contextless" (h: uint) -> Handle {
context = runtime.default_context() context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
exists :: proc(path: string) -> bool { exists :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
is_file :: proc(path: string) -> bool { is_file :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
is_dir :: proc(path: string) -> bool { is_dir :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@@ -86,82 +93,118 @@ is_dir :: proc(path: string) -> bool {
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName // NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
//@private cwd_lock := win32.SRWLOCK{} // zero is initialized //@private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string { get_current_directory :: proc(allocator := context.allocator) -> string {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
change_directory :: proc(path: string) -> (err: Errno) { change_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
remove_directory :: proc(path: string) -> (err: Errno) { remove_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private, require_results)
is_abs :: proc(path: string) -> bool { is_abs :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private, require_results)
fix_long_path :: proc(path: string) -> string { fix_long_path :: proc(path: string) -> string {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
link :: proc(old_name, new_name: string) -> (err: Errno) { link :: proc(old_name, new_name: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
unlink :: proc(path: string) -> (err: Errno) { unlink :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
rename :: proc(old_path, new_path: string) -> (err: Errno) { rename :: proc(old_path, new_path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
truncate :: proc(path: string, length: i64) -> (err: Errno) { truncate :: proc(path: string, length: i64) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
remove :: proc(name: string) -> Errno { remove :: proc(name: string) -> Error {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
pipe :: proc() -> (r, w: Handle, err: Errno) { @(require_results)
pipe :: proc() -> (r, w: Handle, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { @(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
Handle :: distinct uintptr Handle :: distinct uintptr
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct int
_Platform_Error :: enum i32 {
NONE = 0,
FILE_NOT_FOUND = 2,
PATH_NOT_FOUND = 3,
ACCESS_DENIED = 5,
INVALID_HANDLE = 6,
NOT_ENOUGH_MEMORY = 8,
NO_MORE_FILES = 18,
HANDLE_EOF = 38,
NETNAME_DELETED = 64,
FILE_EXISTS = 80,
INVALID_PARAMETER = 87,
BROKEN_PIPE = 109,
BUFFER_OVERFLOW = 111,
INSUFFICIENT_BUFFER = 122,
MOD_NOT_FOUND = 126,
PROC_NOT_FOUND = 127,
DIR_NOT_EMPTY = 145,
ALREADY_EXISTS = 183,
ENVVAR_NOT_FOUND = 203,
MORE_DATA = 234,
OPERATION_ABORTED = 995,
IO_PENDING = 997,
NOT_FOUND = 1168,
PRIVILEGE_NOT_HELD = 1314,
WSAEACCES = 10013,
WSAECONNRESET = 10054,
// Windows reserves errors >= 1<<29 for application use
FILE_IS_PIPE = 1<<29 + 0,
FILE_IS_NOT_DIR = 1<<29 + 1,
NEGATIVE_OFFSET = 1<<29 + 2,
}
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
@@ -182,37 +225,34 @@ O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000 O_CLOEXEC :: 0x80000
ERROR_NONE: Errno : 0 ERROR_FILE_NOT_FOUND :: Platform_Error.FILE_NOT_FOUND
ERROR_FILE_NOT_FOUND: Errno : 2 ERROR_PATH_NOT_FOUND :: Platform_Error.PATH_NOT_FOUND
ERROR_PATH_NOT_FOUND: Errno : 3 ERROR_ACCESS_DENIED :: Platform_Error.ACCESS_DENIED
ERROR_ACCESS_DENIED: Errno : 5 ERROR_INVALID_HANDLE :: Platform_Error.INVALID_HANDLE
ERROR_INVALID_HANDLE: Errno : 6 ERROR_NOT_ENOUGH_MEMORY :: Platform_Error.NOT_ENOUGH_MEMORY
ERROR_NOT_ENOUGH_MEMORY: Errno : 8 ERROR_NO_MORE_FILES :: Platform_Error.NO_MORE_FILES
ERROR_NO_MORE_FILES: Errno : 18 ERROR_HANDLE_EOF :: Platform_Error.HANDLE_EOF
ERROR_HANDLE_EOF: Errno : 38 ERROR_NETNAME_DELETED :: Platform_Error.NETNAME_DELETED
ERROR_NETNAME_DELETED: Errno : 64 ERROR_FILE_EXISTS :: Platform_Error.FILE_EXISTS
ERROR_FILE_EXISTS: Errno : 80 ERROR_INVALID_PARAMETER :: Platform_Error.INVALID_PARAMETER
ERROR_INVALID_PARAMETER: Errno : 87 ERROR_BROKEN_PIPE :: Platform_Error.BROKEN_PIPE
ERROR_BROKEN_PIPE: Errno : 109 ERROR_BUFFER_OVERFLOW :: Platform_Error.BUFFER_OVERFLOW
ERROR_BUFFER_OVERFLOW: Errno : 111 ERROR_INSUFFICIENT_BUFFER :: Platform_Error.INSUFFICIENT_BUFFER
ERROR_INSUFFICIENT_BUFFER: Errno : 122 ERROR_MOD_NOT_FOUND :: Platform_Error.MOD_NOT_FOUND
ERROR_MOD_NOT_FOUND: Errno : 126 ERROR_PROC_NOT_FOUND :: Platform_Error.PROC_NOT_FOUND
ERROR_PROC_NOT_FOUND: Errno : 127 ERROR_DIR_NOT_EMPTY :: Platform_Error.DIR_NOT_EMPTY
ERROR_DIR_NOT_EMPTY: Errno : 145 ERROR_ALREADY_EXISTS :: Platform_Error.ALREADY_EXISTS
ERROR_ALREADY_EXISTS: Errno : 183 ERROR_ENVVAR_NOT_FOUND :: Platform_Error.ENVVAR_NOT_FOUND
ERROR_ENVVAR_NOT_FOUND: Errno : 203 ERROR_MORE_DATA :: Platform_Error.MORE_DATA
ERROR_MORE_DATA: Errno : 234 ERROR_OPERATION_ABORTED :: Platform_Error.OPERATION_ABORTED
ERROR_OPERATION_ABORTED: Errno : 995 ERROR_IO_PENDING :: Platform_Error.IO_PENDING
ERROR_IO_PENDING: Errno : 997 ERROR_NOT_FOUND :: Platform_Error.NOT_FOUND
ERROR_NOT_FOUND: Errno : 1168 ERROR_PRIVILEGE_NOT_HELD :: Platform_Error.PRIVILEGE_NOT_HELD
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314 WSAEACCES :: Platform_Error.WSAEACCES
WSAEACCES: Errno : 10013 WSAECONNRESET :: Platform_Error.WSAECONNRESET
WSAECONNRESET: Errno : 10054
// Windows reserves errors >= 1<<29 for application use ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0 ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
// "Argv" arguments converted to Odin strings // "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@@ -221,20 +261,23 @@ args := _alloc_command_line_arguments()
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@@ -246,6 +289,7 @@ exit :: proc "contextless" (code: int) -> ! {
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
context = runtime.default_context() context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
@@ -253,6 +297,7 @@ current_thread_id :: proc "contextless" () -> int {
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
return nil return nil
} }
+289 -254
View File
@@ -20,148 +20,148 @@ import "base:intrinsics"
// all that about compatibility. But we don't want to push experimental changes // all that about compatibility. But we don't want to push experimental changes
// and have people's code break while it's still work in progress. // and have people's code break while it's still work in progress.
import unix "core:sys/unix" import unix "core:sys/unix"
import linux "core:sys/linux"
Handle :: distinct i32 Handle :: distinct i32
Pid :: distinct i32 Pid :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
Socket :: distinct int Socket :: distinct int
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 _Platform_Error :: linux.Errno
EPERM: Errno : 1 EPERM :: Platform_Error.EPERM
ENOENT: Errno : 2 ENOENT :: Platform_Error.ENOENT
ESRCH: Errno : 3 ESRCH :: Platform_Error.ESRCH
EINTR: Errno : 4 EINTR :: Platform_Error.EINTR
EIO: Errno : 5 EIO :: Platform_Error.EIO
ENXIO: Errno : 6 ENXIO :: Platform_Error.ENXIO
EBADF: Errno : 9 EBADF :: Platform_Error.EBADF
EAGAIN: Errno : 11 EAGAIN :: Platform_Error.EAGAIN
ENOMEM: Errno : 12 ENOMEM :: Platform_Error.ENOMEM
EACCES: Errno : 13 EACCES :: Platform_Error.EACCES
EFAULT: Errno : 14 EFAULT :: Platform_Error.EFAULT
EEXIST: Errno : 17 EEXIST :: Platform_Error.EEXIST
ENODEV: Errno : 19 ENODEV :: Platform_Error.ENODEV
ENOTDIR: Errno : 20 ENOTDIR :: Platform_Error.ENOTDIR
EISDIR: Errno : 21 EISDIR :: Platform_Error.EISDIR
EINVAL: Errno : 22 EINVAL :: Platform_Error.EINVAL
ENFILE: Errno : 23 ENFILE :: Platform_Error.ENFILE
EMFILE: Errno : 24 EMFILE :: Platform_Error.EMFILE
ETXTBSY: Errno : 26 ETXTBSY :: Platform_Error.ETXTBSY
EFBIG: Errno : 27 EFBIG :: Platform_Error.EFBIG
ENOSPC: Errno : 28 ENOSPC :: Platform_Error.ENOSPC
ESPIPE: Errno : 29 ESPIPE :: Platform_Error.ESPIPE
EROFS: Errno : 30 EROFS :: Platform_Error.EROFS
EPIPE: Errno : 32 EPIPE :: Platform_Error.EPIPE
ERANGE: Errno : 34 /* Result too large */ ERANGE :: Platform_Error.ERANGE /* Result too large */
EDEADLK: Errno : 35 /* Resource deadlock would occur */ EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock would occur */
ENAMETOOLONG: Errno : 36 /* File name too long */ ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
ENOLCK: Errno : 37 /* No record locks available */ ENOLCK :: Platform_Error.ENOLCK /* No record locks available */
ENOSYS: Errno : 38 /* Invalid system call number */ ENOSYS :: Platform_Error.ENOSYS /* Invalid system call number */
ENOTEMPTY: Errno : 39 /* Directory not empty */ ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
ELOOP: Errno : 40 /* Too many symbolic links encountered */ ELOOP :: Platform_Error.ELOOP /* Too many symbolic links encountered */
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ EWOULDBLOCK :: Platform_Error.EWOULDBLOCK /* Operation would block */
ENOMSG: Errno : 42 /* No message of desired type */ ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
EIDRM: Errno : 43 /* Identifier removed */ EIDRM :: Platform_Error.EIDRM /* Identifier removed */
ECHRNG: Errno : 44 /* Channel number out of range */ ECHRNG :: Platform_Error.ECHRNG /* Channel number out of range */
EL2NSYNC: Errno : 45 /* Level 2 not synchronized */ EL2NSYNC :: Platform_Error.EL2NSYNC /* Level 2 not synchronized */
EL3HLT: Errno : 46 /* Level 3 halted */ EL3HLT :: Platform_Error.EL3HLT /* Level 3 halted */
EL3RST: Errno : 47 /* Level 3 reset */ EL3RST :: Platform_Error.EL3RST /* Level 3 reset */
ELNRNG: Errno : 48 /* Link number out of range */ ELNRNG :: Platform_Error.ELNRNG /* Link number out of range */
EUNATCH: Errno : 49 /* Protocol driver not attached */ EUNATCH :: Platform_Error.EUNATCH /* Protocol driver not attached */
ENOCSI: Errno : 50 /* No CSI structure available */ ENOCSI :: Platform_Error.ENOCSI /* No CSI structure available */
EL2HLT: Errno : 51 /* Level 2 halted */ EL2HLT :: Platform_Error.EL2HLT /* Level 2 halted */
EBADE: Errno : 52 /* Invalid exchange */ EBADE :: Platform_Error.EBADE /* Invalid exchange */
EBADR: Errno : 53 /* Invalid request descriptor */ EBADR :: Platform_Error.EBADR /* Invalid request descriptor */
EXFULL: Errno : 54 /* Exchange full */ EXFULL :: Platform_Error.EXFULL /* Exchange full */
ENOANO: Errno : 55 /* No anode */ ENOANO :: Platform_Error.ENOANO /* No anode */
EBADRQC: Errno : 56 /* Invalid request code */ EBADRQC :: Platform_Error.EBADRQC /* Invalid request code */
EBADSLT: Errno : 57 /* Invalid slot */ EBADSLT :: Platform_Error.EBADSLT /* Invalid slot */
EDEADLOCK: Errno : EDEADLK EDEADLOCK :: Platform_Error.EDEADLOCK
EBFONT: Errno : 59 /* Bad font file format */ EBFONT :: Platform_Error.EBFONT /* Bad font file format */
ENOSTR: Errno : 60 /* Device not a stream */ ENOSTR :: Platform_Error.ENOSTR /* Device not a stream */
ENODATA: Errno : 61 /* No data available */ ENODATA :: Platform_Error.ENODATA /* No data available */
ETIME: Errno : 62 /* Timer expired */ ETIME :: Platform_Error.ETIME /* Timer expired */
ENOSR: Errno : 63 /* Out of streams resources */ ENOSR :: Platform_Error.ENOSR /* Out of streams resources */
ENONET: Errno : 64 /* Machine is not on the network */ ENONET :: Platform_Error.ENONET /* Machine is not on the network */
ENOPKG: Errno : 65 /* Package not installed */ ENOPKG :: Platform_Error.ENOPKG /* Package not installed */
EREMOTE: Errno : 66 /* Object is remote */ EREMOTE :: Platform_Error.EREMOTE /* Object is remote */
ENOLINK: Errno : 67 /* Link has been severed */ ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
EADV: Errno : 68 /* Advertise error */ EADV :: Platform_Error.EADV /* Advertise error */
ESRMNT: Errno : 69 /* Srmount error */ ESRMNT :: Platform_Error.ESRMNT /* Srmount error */
ECOMM: Errno : 70 /* Communication error on send */ ECOMM :: Platform_Error.ECOMM /* Communication error on send */
EPROTO: Errno : 71 /* Protocol error */ EPROTO :: Platform_Error.EPROTO /* Protocol error */
EMULTIHOP: Errno : 72 /* Multihop attempted */ EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
EDOTDOT: Errno : 73 /* RFS specific error */ EDOTDOT :: Platform_Error.EDOTDOT /* RFS specific error */
EBADMSG: Errno : 74 /* Not a data message */ EBADMSG :: Platform_Error.EBADMSG /* Not a data message */
EOVERFLOW: Errno : 75 /* Value too large for defined data type */ EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large for defined data type */
ENOTUNIQ: Errno : 76 /* Name not unique on network */ ENOTUNIQ :: Platform_Error.ENOTUNIQ /* Name not unique on network */
EBADFD: Errno : 77 /* File descriptor in bad state */ EBADFD :: Platform_Error.EBADFD /* File descriptor in bad state */
EREMCHG: Errno : 78 /* Remote address changed */ EREMCHG :: Platform_Error.EREMCHG /* Remote address changed */
ELIBACC: Errno : 79 /* Can not access a needed shared library */ ELIBACC :: Platform_Error.ELIBACC /* Can not access a needed shared library */
ELIBBAD: Errno : 80 /* Accessing a corrupted shared library */ ELIBBAD :: Platform_Error.ELIBBAD /* Accessing a corrupted shared library */
ELIBSCN: Errno : 81 /* .lib section in a.out corrupted */ ELIBSCN :: Platform_Error.ELIBSCN /* .lib section in a.out corrupted */
ELIBMAX: Errno : 82 /* Attempting to link in too many shared libraries */ ELIBMAX :: Platform_Error.ELIBMAX /* Attempting to link in too many shared libraries */
ELIBEXEC: Errno : 83 /* Cannot exec a shared library directly */ ELIBEXEC :: Platform_Error.ELIBEXEC /* Cannot exec a shared library directly */
EILSEQ: Errno : 84 /* Illegal byte sequence */ EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */
ERESTART: Errno : 85 /* Interrupted system call should be restarted */ ERESTART :: Platform_Error.ERESTART /* Interrupted system call should be restarted */
ESTRPIPE: Errno : 86 /* Streams pipe error */ ESTRPIPE :: Platform_Error.ESTRPIPE /* Streams pipe error */
EUSERS: Errno : 87 /* Too many users */ EUSERS :: Platform_Error.EUSERS /* Too many users */
ENOTSOCK: Errno : 88 /* Socket operation on non-socket */ ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 89 /* Destination address required */ EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
EMSGSIZE: Errno : 90 /* Message too long */ EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
EPROTOTYPE: Errno : 91 /* Protocol wrong type for socket */ EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 92 /* Protocol not available */ ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol not available */
EPROTONOSUPPORT:Errno : 93 /* Protocol not supported */ EPROTONOSUPPOR :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
ESOCKTNOSUPPORT:Errno : 94 /* Socket type not supported */ ESOCKTNOSUPPOR :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
EOPNOTSUPP: Errno : 95 /* Operation not supported on transport endpoint */ EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported on transport endpoint */
EPFNOSUPPORT: Errno : 96 /* Protocol family not supported */ EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
EAFNOSUPPORT: Errno : 97 /* Address family not supported by protocol */ EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol */
EADDRINUSE: Errno : 98 /* Address already in use */ EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
EADDRNOTAVAIL: Errno : 99 /* Cannot assign requested address */ EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Cannot assign requested address */
ENETDOWN: Errno : 100 /* Network is down */ ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
ENETUNREACH: Errno : 101 /* Network is unreachable */ ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
ENETRESET: Errno : 102 /* Network dropped connection because of reset */ ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection because of reset */
ECONNABORTED: Errno : 103 /* Software caused connection abort */ ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
ECONNRESET: Errno : 104 /* Connection reset by peer */ ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
ENOBUFS: Errno : 105 /* No buffer space available */ ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
EISCONN: Errno : 106 /* Transport endpoint is already connected */ EISCONN :: Platform_Error.EISCONN /* Transport endpoint is already connected */
ENOTCONN: Errno : 107 /* Transport endpoint is not connected */ ENOTCONN :: Platform_Error.ENOTCONN /* Transport endpoint is not connected */
ESHUTDOWN: Errno : 108 /* Cannot send after transport endpoint shutdown */ ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Cannot send after transport endpoint shutdown */
ETOOMANYREFS: Errno : 109 /* Too many references: cannot splice */ ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: cannot splice */
ETIMEDOUT: Errno : 110 /* Connection timed out */ ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Connection timed out */
ECONNREFUSED: Errno : 111 /* Connection refused */ ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
EHOSTDOWN: Errno : 112 /* Host is down */ EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
EHOSTUNREACH: Errno : 113 /* No route to host */ EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
EALREADY: Errno : 114 /* Operation already in progress */ EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
EINPROGRESS: Errno : 115 /* Operation now in progress */ EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
ESTALE: Errno : 116 /* Stale file handle */ ESTALE :: Platform_Error.ESTALE /* Stale file handle */
EUCLEAN: Errno : 117 /* Structure needs cleaning */ EUCLEAN :: Platform_Error.EUCLEAN /* Structure needs cleaning */
ENOTNAM: Errno : 118 /* Not a XENIX named type file */ ENOTNAM :: Platform_Error.ENOTNAM /* Not a XENIX named type file */
ENAVAIL: Errno : 119 /* No XENIX semaphores available */ ENAVAIL :: Platform_Error.ENAVAIL /* No XENIX semaphores available */
EISNAM: Errno : 120 /* Is a named type file */ EISNAM :: Platform_Error.EISNAM /* Is a named type file */
EREMOTEIO: Errno : 121 /* Remote I/O error */ EREMOTEIO :: Platform_Error.EREMOTEIO /* Remote I/O error */
EDQUOT: Errno : 122 /* Quota exceeded */ EDQUOT :: Platform_Error.EDQUOT /* Quota exceeded */
ENOMEDIUM: Errno : 123 /* No medium found */ ENOMEDIUM :: Platform_Error.ENOMEDIUM /* No medium found */
EMEDIUMTYPE: Errno : 124 /* Wrong medium type */ EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE /* Wrong medium type */
ECANCELED: Errno : 125 /* Operation Canceled */ ECANCELED :: Platform_Error.ECANCELED /* Operation Canceled */
ENOKEY: Errno : 126 /* Required key not available */ ENOKEY :: Platform_Error.ENOKEY /* Required key not available */
EKEYEXPIRED: Errno : 127 /* Key has expired */ EKEYEXPIRED :: Platform_Error.EKEYEXPIRED /* Key has expired */
EKEYREVOKED: Errno : 128 /* Key has been revoked */ EKEYREVOKED :: Platform_Error.EKEYREVOKED /* Key has been revoked */
EKEYREJECTED: Errno : 129 /* Key was rejected by service */ EKEYREJECTED :: Platform_Error.EKEYREJECTED /* Key was rejected by service */
/* for robust mutexes */ /* for robust mutexes */
EOWNERDEAD: Errno : 130 /* Owner died */ EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Owner died */
ENOTRECOVERABLE: Errno : 131 /* State not recoverable */ ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE /* State not recoverable */
ERFKILL: Errno : 132 /* Operation not possible due to RF-kill */ ERFKILL :: Platform_Error.ERFKILL /* Operation not possible due to RF-kill */
EHWPOISON: Errno : 133 /* Memory page has hardware error */ EHWPOISON :: Platform_Error.EHWPOISON /* Memory page has hardware error */
ADDR_NO_RANDOMIZE :: 0x40000 ADDR_NO_RANDOMIZE :: 0x40000
@@ -448,13 +448,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -506,41 +506,55 @@ foreign dl {
@(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) --- @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) ---
} }
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
// determine errno from syscall return value // determine errno from syscall return value
@private @(private, require_results)
_get_errno :: proc(res: int) -> Errno { _get_errno :: proc(res: int) -> Error {
if res < 0 && res > -4096 { if res < 0 && res > -4096 {
return Errno(-res) return Platform_Error(-res)
} }
return 0 return nil
} }
// get errno from libc // get errno from libc
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__errno_location()^) get_last_error :: proc "contextless" () -> Error {
err := Platform_Error(__errno_location()^)
#partial switch err {
case .NONE:
return nil
case .EPERM:
return .Permission_Denied
case .EEXIST:
return .Exist
case .ENOENT:
return .Not_Exist
}
return err
} }
personality :: proc(persona: u64) -> (Errno) { personality :: proc(persona: u64) -> Error {
res := unix.sys_personality(persona) res := unix.sys_personality(persona)
if res == -1 { if res == -1 {
return _get_errno(res) return _get_errno(res)
} }
return ERROR_NONE return nil
} }
fork :: proc() -> (Pid, Errno) { @(require_results)
fork :: proc() -> (Pid, Error) {
pid := unix.sys_fork() pid := unix.sys_fork()
if pid == -1 { if pid == -1 {
return -1, _get_errno(pid) return -1, _get_errno(pid)
} }
return Pid(pid), ERROR_NONE return Pid(pid), nil
} }
execvp :: proc(path: string, args: []string) -> Errno { execvp :: proc(path: string, args: []string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -551,24 +565,30 @@ execvp :: proc(path: string, args: []string) -> Errno {
} }
_unix_execvp(path_cstr, raw_data(args_cstrs)) _unix_execvp(path_cstr, raw_data(args_cstrs))
return Errno(get_last_error()) return get_last_error()
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := unix.sys_open(cstr, flags, uint(mode)) handle := unix.sys_open(cstr, flags, uint(mode))
if handle < 0 { if handle < 0 {
return INVALID_HANDLE, _get_errno(handle) return INVALID_HANDLE, _get_errno(handle)
} }
return Handle(handle), ERROR_NONE return Handle(handle), nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
return _get_errno(unix.sys_close(int(fd))) return _get_errno(unix.sys_close(int(fd)))
} }
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
}
// If you read or write more than `SSIZE_MAX` bytes, result is implementation defined (probably an error). // If you read or write more than `SSIZE_MAX` bytes, result is implementation defined (probably an error).
// `SSIZE_MAX` is also implementation defined but usually the max of a `ssize_t` which is `max(int)` in Odin. // `SSIZE_MAX` is also implementation defined but usually the max of a `ssize_t` which is `max(int)` in Odin.
// In practice a read/write call would probably never read/write these big buffers all at once, // In practice a read/write call would probably never read/write these big buffers all at once,
@@ -578,9 +598,9 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_read := min(uint(len(data)), MAX_RW) to_read := min(uint(len(data)), MAX_RW)
@@ -589,12 +609,12 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if bytes_read < 0 { if bytes_read < 0 {
return -1, _get_errno(bytes_read) return -1, _get_errno(bytes_read)
} }
return bytes_read, ERROR_NONE return bytes_read, nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(uint(len(data)), MAX_RW) to_write := min(uint(len(data)), MAX_RW)
@@ -603,12 +623,12 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if bytes_written < 0 { if bytes_written < 0 {
return -1, _get_errno(bytes_written) return -1, _get_errno(bytes_written)
} }
return bytes_written, ERROR_NONE return bytes_written, nil
} }
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_read := min(uint(len(data)), MAX_RW) to_read := min(uint(len(data)), MAX_RW)
@@ -617,12 +637,12 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if bytes_read < 0 { if bytes_read < 0 {
return -1, _get_errno(bytes_read) return -1, _get_errno(bytes_read)
} }
return bytes_read, ERROR_NONE return bytes_read, nil
} }
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(uint(len(data)), MAX_RW) to_write := min(uint(len(data)), MAX_RW)
@@ -631,92 +651,97 @@ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if bytes_written < 0 { if bytes_written < 0 {
return -1, _get_errno(bytes_written) return -1, _get_errno(bytes_written)
} }
return bytes_written, ERROR_NONE return bytes_written, nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := unix.sys_lseek(int(fd), offset, whence) res := unix.sys_lseek(int(fd), offset, whence)
if res < 0 { if res < 0 {
return -1, _get_errno(int(res)) return -1, _get_errno(int(res))
} }
return i64(res), ERROR_NONE return i64(res), nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
// deliberately uninitialized; the syscall fills this buffer for us // deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = --- s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s)) result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return max(s.size, 0), ERROR_NONE return max(s.size, 0), nil
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr)) return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr))
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_unlink(path_cstr)) return _get_errno(unix.sys_unlink(path_cstr))
} }
make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno { make_directory :: proc(path: string, mode: u32 = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_mkdir(path_cstr, uint(mode))) return _get_errno(unix.sys_mkdir(path_cstr, uint(mode)))
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_rmdir(path_cstr)) return _get_errno(unix.sys_rmdir(path_cstr))
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -725,6 +750,7 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
is_file :: proc {is_file_path, is_file_handle} is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle} is_dir :: proc {is_dir_path, is_dir_handle}
@(require_results)
exists :: proc(path: string) -> bool { exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator) cpath := strings.clone_to_cstring(path, context.temp_allocator)
@@ -742,26 +768,22 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {}
*/ */
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
s, err := _fstat(fd) last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _fstat(fd) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
s, err := _stat(name) last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _stat(name) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -771,11 +793,11 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
if result < 0 { if result < 0 {
return s, _get_errno(result) return s, _get_errno(result)
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -785,53 +807,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
if result < 0 { if result < 0 {
return s, _get_errno(result) return s, _get_errno(result)
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized; the syscall fills this buffer for us // deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = --- s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s)) result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 { if result < 0 {
return s, _get_errno(result) return s, _get_errno(result)
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE err = nil
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -842,8 +864,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -860,12 +882,13 @@ _readlink :: proc(path: string) -> (string, Errno) {
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
buf : [256]byte buf : [256]byte
fd_str := strconv.itoa( buf[:], cast(int)fd ) fd_str := strconv.itoa( buf[:], cast(int)fd )
@@ -875,7 +898,8 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
return _readlink(procfs_path) return _readlink(procfs_path)
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -886,25 +910,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := unix.sys_access(cstr, mask) result := unix.sys_access(cstr, mask)
if result < 0 { if result < 0 {
return false, _get_errno(result) return false, _get_errno(result)
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator) path_str := strings.clone_to_cstring(key, context.temp_allocator)
@@ -916,33 +941,35 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
set_env :: proc(key, value: string) -> Errno { set_env :: proc(key, value: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
key_cstring := strings.clone_to_cstring(key, context.temp_allocator) key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
value_cstring := strings.clone_to_cstring(value, context.temp_allocator) value_cstring := strings.clone_to_cstring(value, context.temp_allocator)
// NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly
res := _unix_setenv(key_cstring, value_cstring, 1) res := _unix_setenv(key_cstring, value_cstring, 1)
if res < 0 { if res < 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
unset_env :: proc(key: string) -> Errno { unset_env :: proc(key: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
s := strings.clone_to_cstring(key, context.temp_allocator) s := strings.clone_to_cstring(key, context.temp_allocator)
res := _unix_putenv(s) res := _unix_putenv(s)
if res < 0 { if res < 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems. // an authoritative value for it across all systems.
@@ -964,14 +991,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := unix.sys_chdir(cstr) res := unix.sys_chdir(cstr)
if res < 0 { if res < 0 {
return _get_errno(res) return _get_errno(res)
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -979,16 +1006,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid() return unix.sys_gettid()
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags)) handle := _unix_dlopen(cstr, c.int(flags))
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -1004,6 +1034,7 @@ dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -1016,11 +1047,12 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
return int(_unix_get_nprocs()) return int(_unix_get_nprocs())
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
@@ -1029,117 +1061,120 @@ _alloc_command_line_arguments :: proc() -> []string {
return res return res
} }
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Errno) { @(require_results)
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) {
result := unix.sys_socket(domain, type, protocol) result := unix.sys_socket(domain, type, protocol)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return Socket(result), ERROR_NONE return Socket(result), nil
} }
bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) { bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
result := unix.sys_bind(int(sd), addr, len) result := unix.sys_bind(int(sd), addr, len)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) { connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
result := unix.sys_connect(int(sd), addr, len) result := unix.sys_connect(int(sd), addr, len)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Errno) { accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Error) {
result := unix.sys_accept(int(sd), rawptr(addr), len) result := unix.sys_accept(int(sd), rawptr(addr), len)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return Socket(result), ERROR_NONE return Socket(result), nil
} }
listen :: proc(sd: Socket, backlog: int) -> (Errno) { listen :: proc(sd: Socket, backlog: int) -> Error {
result := unix.sys_listen(int(sd), backlog) result := unix.sys_listen(int(sd), backlog)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> (Errno) { setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error {
result := unix.sys_setsockopt(int(sd), level, optname, optval, optlen) result := unix.sys_setsockopt(int(sd), level, optname, optval, optlen)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Errno) { recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Error) {
result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, addr, uintptr(addr_size)) result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, addr, uintptr(addr_size))
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) { recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, nil, 0) result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, nil, 0)
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Errno) { sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Error) {
result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen) result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen)
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) { send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0) result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0)
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
shutdown :: proc(sd: Socket, how: int) -> (Errno) { shutdown :: proc(sd: Socket, how: int) -> Error {
result := unix.sys_shutdown(int(sd), how) result := unix.sys_shutdown(int(sd), how)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) {
result := unix.sys_fcntl(fd, cmd, arg) result := unix.sys_fcntl(fd, cmd, arg)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return result, ERROR_NONE return result, nil
} }
poll :: proc(fds: []pollfd, timeout: int) -> (int, Errno) { @(require_results)
poll :: proc(fds: []pollfd, timeout: int) -> (int, Error) {
result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout) result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return result, ERROR_NONE return result, nil
} }
ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) { @(require_results)
ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Error) {
result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t)) result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t))
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return result, ERROR_NONE return result, nil
} }
+392 -217
View File
@@ -9,150 +9,289 @@ import "core:c"
Handle :: distinct i32 Handle :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 /* No error */ _Platform_Error :: enum i32 {
EPERM: Errno : 1 /* Operation not permitted */ NONE = 0,
ENOENT: Errno : 2 /* No such file or directory */ EPERM = 1, /* Operation not permitted */
EINTR: Errno : 4 /* Interrupted system call */ ENOENT = 2, /* No such file or directory */
ESRCH: Errno : 3 /* No such process */ EINTR = 4, /* Interrupted system call */
EIO: Errno : 5 /* Input/output error */ ESRCH = 3, /* No such process */
ENXIO: Errno : 6 /* Device not configured */ EIO = 5, /* Input/output error */
E2BIG: Errno : 7 /* Argument list too long */ ENXIO = 6, /* Device not configured */
ENOEXEC: Errno : 8 /* Exec format error */ E2BIG = 7, /* Argument list too long */
EBADF: Errno : 9 /* Bad file descriptor */ ENOEXEC = 8, /* Exec format error */
ECHILD: Errno : 10 /* No child processes */ EBADF = 9, /* Bad file descriptor */
EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */ ECHILD = 10, /* No child processes */
ENOMEM: Errno : 12 /* Cannot allocate memory */ EDEADLK = 11, /* Resource deadlock avoided. 11 was EAGAIN */
EACCES: Errno : 13 /* Permission denied */ ENOMEM = 12, /* Cannot allocate memory */
EFAULT: Errno : 14 /* Bad address */ EACCES = 13, /* Permission denied */
ENOTBLK: Errno : 15 /* Block device required */ EFAULT = 14, /* Bad address */
EBUSY: Errno : 16 /* Device busy */ ENOTBLK = 15, /* Block device required */
EEXIST: Errno : 17 /* File exists */ EBUSY = 16, /* Device busy */
EXDEV: Errno : 18 /* Cross-device link */ EEXIST = 17, /* File exists */
ENODEV: Errno : 19 /* Operation not supported by device */ EXDEV = 18, /* Cross-device link */
ENOTDIR: Errno : 20 /* Not a directory */ ENODEV = 19, /* Operation not supported by device */
EISDIR: Errno : 21 /* Is a directory */ ENOTDIR = 20, /* Not a directory */
EINVAL: Errno : 22 /* Invalid argument */ EISDIR = 21, /* Is a directory */
ENFILE: Errno : 23 /* Too many open files in system */ EINVAL = 22, /* Invalid argument */
EMFILE: Errno : 24 /* Too many open files */ ENFILE = 23, /* Too many open files in system */
ENOTTY: Errno : 25 /* Inappropriate ioctl for device */ EMFILE = 24, /* Too many open files */
ETXTBSY: Errno : 26 /* Text file busy */ ENOTTY = 25, /* Inappropriate ioctl for device */
EFBIG: Errno : 27 /* File too large */ ETXTBSY = 26, /* Text file busy */
ENOSPC: Errno : 28 /* No space left on device */ EFBIG = 27, /* File too large */
ESPIPE: Errno : 29 /* Illegal seek */ ENOSPC = 28, /* No space left on device */
EROFS: Errno : 30 /* Read-only file system */ ESPIPE = 29, /* Illegal seek */
EMLINK: Errno : 31 /* Too many links */ EROFS = 30, /* Read-only file system */
EPIPE: Errno : 32 /* Broken pipe */ EMLINK = 31, /* Too many links */
EPIPE = 32, /* Broken pipe */
/* math software */
EDOM = 33, /* Numerical argument out of domain */
ERANGE = 34, /* Result too large or too small */
/* non-blocking and interrupt i/o */
EAGAIN = 35, /* Resource temporarily unavailable */
EWOULDBLOCK = EAGAIN, /* Operation would block */
EINPROGRESS = 36, /* Operation now in progress */
EALREADY = 37, /* Operation already in progress */
/* ipc/network software -- argument errors */
ENOTSOCK = 38, /* Socket operation on non-socket */
EDESTADDRREQ = 39, /* Destination address required */
EMSGSIZE = 40, /* Message too long */
EPROTOTYPE = 41, /* Protocol wrong type for socket */
ENOPROTOOPT = 42, /* Protocol option not available */
EPROTONOSUPPORT = 43, /* Protocol not supported */
ESOCKTNOSUPPORT = 44, /* Socket type not supported */
EOPNOTSUPP = 45, /* Operation not supported */
EPFNOSUPPORT = 46, /* Protocol family not supported */
EAFNOSUPPORT = 47, /* Address family not supported by protocol family */
EADDRINUSE = 48, /* Address already in use */
EADDRNOTAVAIL = 49, /* Can't assign requested address */
/* ipc/network software -- operational errors */
ENETDOWN = 50, /* Network is down */
ENETUNREACH = 51, /* Network is unreachable */
ENETRESET = 52, /* Network dropped connection on reset */
ECONNABORTED = 53, /* Software caused connection abort */
ECONNRESET = 54, /* Connection reset by peer */
ENOBUFS = 55, /* No buffer space available */
EISCONN = 56, /* Socket is already connected */
ENOTCONN = 57, /* Socket is not connected */
ESHUTDOWN = 58, /* Can't send after socket shutdown */
ETOOMANYREFS = 59, /* Too many references: can't splice */
ETIMEDOUT = 60, /* Operation timed out */
ECONNREFUSED = 61, /* Connection refused */
ELOOP = 62, /* Too many levels of symbolic links */
ENAMETOOLONG = 63, /* File name too long */
/* should be rearranged */
EHOSTDOWN = 64, /* Host is down */
EHOSTUNREACH = 65, /* No route to host */
ENOTEMPTY = 66, /* Directory not empty */
/* quotas & mush */
EPROCLIM = 67, /* Too many processes */
EUSERS = 68, /* Too many users */
EDQUOT = 69, /* Disc quota exceeded */
/* Network File System */
ESTALE = 70, /* Stale NFS file handle */
EREMOTE = 71, /* Too many levels of remote in path */
EBADRPC = 72, /* RPC struct is bad */
ERPCMISMATCH = 73, /* RPC version wrong */
EPROGUNAVAIL = 74, /* RPC prog. not avail */
EPROGMISMATCH = 75, /* Program version wrong */
EPROCUNAVAIL = 76, /* Bad procedure for program */
ENOLCK = 77, /* No locks available */
ENOSYS = 78, /* Function not implemented */
EFTYPE = 79, /* Inappropriate file type or format */
EAUTH = 80, /* Authentication error */
ENEEDAUTH = 81, /* Need authenticator */
/* SystemV IPC */
EIDRM = 82, /* Identifier removed */
ENOMSG = 83, /* No message of desired type */
EOVERFLOW = 84, /* Value too large to be stored in data type */
/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */
EILSEQ = 85, /* Illegal byte sequence */
/* From IEEE Std 1003.1-2001 */
/* Base, Realtime, Threads or Thread Priority Scheduling option errors */
ENOTSUP = 86, /* Not supported */
/* Realtime option errors */
ECANCELED = 87, /* Operation canceled */
/* Realtime, XSI STREAMS option errors */
EBADMSG = 88, /* Bad or Corrupt message */
/* XSI STREAMS option errors */
ENODATA = 89, /* No message available */
ENOSR = 90, /* No STREAM resources */
ENOSTR = 91, /* Not a STREAM */
ETIME = 92, /* STREAM ioctl timeout */
/* File system extended attribute errors */
ENOATTR = 93, /* Attribute not found */
/* Realtime, XSI STREAMS option errors */
EMULTIHOP = 94, /* Multihop attempted */
ENOLINK = 95, /* Link has been severed */
EPROTO = 96, /* Protocol error */
/* Robust mutexes */
EOWNERDEAD = 97, /* Previous owner died */
ENOTRECOVERABLE = 98, /* State not recoverable */
ELAST = 98, /* Must equal largest Error */
}
EPERM :: Platform_Error.EPERM /* Operation not permitted */
ENOENT :: Platform_Error.ENOENT /* No such file or directory */
EINTR :: Platform_Error.EINTR /* Interrupted system call */
ESRCH :: Platform_Error.ESRCH /* No such process */
EIO :: Platform_Error.EIO /* Input/output error */
ENXIO :: Platform_Error.ENXIO /* Device not configured */
E2BIG :: Platform_Error.E2BIG /* Argument list too long */
ENOEXEC :: Platform_Error.ENOEXEC /* Exec format error */
EBADF :: Platform_Error.EBADF /* Bad file descriptor */
ECHILD :: Platform_Error.ECHILD /* No child processes */
EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock avoided. 11 was EAGAIN */
ENOMEM :: Platform_Error.ENOMEM /* Cannot allocate memory */
EACCES :: Platform_Error.EACCES /* Permission denied */
EFAULT :: Platform_Error.EFAULT /* Bad address */
ENOTBLK :: Platform_Error.ENOTBLK /* Block device required */
EBUSY :: Platform_Error.EBUSY /* Device busy */
EEXIST :: Platform_Error.EEXIST /* File exists */
EXDEV :: Platform_Error.EXDEV /* Cross-device link */
ENODEV :: Platform_Error.ENODEV /* Operation not supported by device */
ENOTDIR :: Platform_Error.ENOTDIR /* Not a directory */
EISDIR :: Platform_Error.EISDIR /* Is a directory */
EINVAL :: Platform_Error.EINVAL /* Invalid argument */
ENFILE :: Platform_Error.ENFILE /* Too many open files in system */
EMFILE :: Platform_Error.EMFILE /* Too many open files */
ENOTTY :: Platform_Error.ENOTTY /* Inappropriate ioctl for device */
ETXTBSY :: Platform_Error.ETXTBSY /* Text file busy */
EFBIG :: Platform_Error.EFBIG /* File too large */
ENOSPC :: Platform_Error.ENOSPC /* No space left on device */
ESPIPE :: Platform_Error.ESPIPE /* Illegal seek */
EROFS :: Platform_Error.EROFS /* Read-only file system */
EMLINK :: Platform_Error.EMLINK /* Too many links */
EPIPE :: Platform_Error.EPIPE /* Broken pipe */
/* math software */ /* math software */
EDOM: Errno : 33 /* Numerical argument out of domain */ EDOM :: Platform_Error.EDOM /* Numerical argument out of domain */
ERANGE: Errno : 34 /* Result too large or too small */ ERANGE :: Platform_Error.ERANGE /* Result too large or too small */
/* non-blocking and interrupt i/o */ /* non-blocking and interrupt i/o */
EAGAIN: Errno : 35 /* Resource temporarily unavailable */ EAGAIN :: Platform_Error.EAGAIN /* Resource temporarily unavailable */
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ EWOULDBLOCK :: EAGAIN /* Operation would block */
EINPROGRESS: Errno : 36 /* Operation now in progress */ EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
EALREADY: Errno : 37 /* Operation already in progress */ EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
/* ipc/network software -- argument errors */ /* ipc/network software -- argument errors */
ENOTSOCK: Errno : 38 /* Socket operation on non-socket */ ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 39 /* Destination address required */ EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
EMSGSIZE: Errno : 40 /* Message too long */ EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */ EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 42 /* Protocol option not available */ ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol option not available */
EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */ EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */ ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
EOPNOTSUPP: Errno : 45 /* Operation not supported */ EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported */
EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */ EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */ EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol family */
EADDRINUSE: Errno : 48 /* Address already in use */ EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */ EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Can't assign requested address */
/* ipc/network software -- operational errors */ /* ipc/network software -- operational errors */
ENETDOWN: Errno : 50 /* Network is down */ ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
ENETUNREACH: Errno : 51 /* Network is unreachable */ ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
ENETRESET: Errno : 52 /* Network dropped connection on reset */ ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection on reset */
ECONNABORTED: Errno : 53 /* Software caused connection abort */ ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
ECONNRESET: Errno : 54 /* Connection reset by peer */ ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
ENOBUFS: Errno : 55 /* No buffer space available */ ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
EISCONN: Errno : 56 /* Socket is already connected */ EISCONN :: Platform_Error.EISCONN /* Socket is already connected */
ENOTCONN: Errno : 57 /* Socket is not connected */ ENOTCONN :: Platform_Error.ENOTCONN /* Socket is not connected */
ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */ ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Can't send after socket shutdown */
ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */ ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: can't splice */
ETIMEDOUT: Errno : 60 /* Operation timed out */ ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Operation timed out */
ECONNREFUSED: Errno : 61 /* Connection refused */ ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
ELOOP: Errno : 62 /* Too many levels of symbolic links */ ELOOP :: Platform_Error.ELOOP /* Too many levels of symbolic links */
ENAMETOOLONG: Errno : 63 /* File name too long */ ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
/* should be rearranged */ /* should be rearranged */
EHOSTDOWN: Errno : 64 /* Host is down */ EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
EHOSTUNREACH: Errno : 65 /* No route to host */ EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
ENOTEMPTY: Errno : 66 /* Directory not empty */ ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
/* quotas & mush */ /* quotas & mush */
EPROCLIM: Errno : 67 /* Too many processes */ EPROCLIM :: Platform_Error.EPROCLIM /* Too many processes */
EUSERS: Errno : 68 /* Too many users */ EUSERS :: Platform_Error.EUSERS /* Too many users */
EDQUOT: Errno : 69 /* Disc quota exceeded */ EDQUOT :: Platform_Error.EDQUOT /* Disc quota exceeded */
/* Network File System */ /* Network File System */
ESTALE: Errno : 70 /* Stale NFS file handle */ ESTALE :: Platform_Error.ESTALE /* Stale NFS file handle */
EREMOTE: Errno : 71 /* Too many levels of remote in path */ EREMOTE :: Platform_Error.EREMOTE /* Too many levels of remote in path */
EBADRPC: Errno : 72 /* RPC struct is bad */ EBADRPC :: Platform_Error.EBADRPC /* RPC struct is bad */
ERPCMISMATCH: Errno : 73 /* RPC version wrong */ ERPCMISMATCH :: Platform_Error.ERPCMISMATCH /* RPC version wrong */
EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */ EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL /* RPC prog. not avail */
EPROGMISMATCH: Errno : 75 /* Program version wrong */ EPROGMISMATCH :: Platform_Error.EPROGMISMATCH /* Program version wrong */
EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */ EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL /* Bad procedure for program */
ENOLCK: Errno : 77 /* No locks available */ ENOLCK :: Platform_Error.ENOLCK /* No locks available */
ENOSYS: Errno : 78 /* Function not implemented */ ENOSYS :: Platform_Error.ENOSYS /* Function not implemented */
EFTYPE: Errno : 79 /* Inappropriate file type or format */ EFTYPE :: Platform_Error.EFTYPE /* Inappropriate file type or format */
EAUTH: Errno : 80 /* Authentication error */ EAUTH :: Platform_Error.EAUTH /* Authentication error */
ENEEDAUTH: Errno : 81 /* Need authenticator */ ENEEDAUTH :: Platform_Error.ENEEDAUTH /* Need authenticator */
/* SystemV IPC */ /* SystemV IPC */
EIDRM: Errno : 82 /* Identifier removed */ EIDRM :: Platform_Error.EIDRM /* Identifier removed */
ENOMSG: Errno : 83 /* No message of desired type */ ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */ EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large to be stored in data type */
/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ /* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */
EILSEQ: Errno : 85 /* Illegal byte sequence */ EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */
/* From IEEE Std 1003.1-2001 */ /* From IEEE Std 1003.1-2001 */
/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ /* Base, Realtime, Threads or Thread Priority Scheduling option errors */
ENOTSUP: Errno : 86 /* Not supported */ ENOTSUP :: Platform_Error.ENOTSUP /* Not supported */
/* Realtime option errors */ /* Realtime option errors */
ECANCELED: Errno : 87 /* Operation canceled */ ECANCELED :: Platform_Error.ECANCELED /* Operation canceled */
/* Realtime, XSI STREAMS option errors */ /* Realtime, XSI STREAMS option errors */
EBADMSG: Errno : 88 /* Bad or Corrupt message */ EBADMSG :: Platform_Error.EBADMSG /* Bad or Corrupt message */
/* XSI STREAMS option errors */ /* XSI STREAMS option errors */
ENODATA: Errno : 89 /* No message available */ ENODATA :: Platform_Error.ENODATA /* No message available */
ENOSR: Errno : 90 /* No STREAM resources */ ENOSR :: Platform_Error.ENOSR /* No STREAM resources */
ENOSTR: Errno : 91 /* Not a STREAM */ ENOSTR :: Platform_Error.ENOSTR /* Not a STREAM */
ETIME: Errno : 92 /* STREAM ioctl timeout */ ETIME :: Platform_Error.ETIME /* STREAM ioctl timeout */
/* File system extended attribute errors */ /* File system extended attribute errors */
ENOATTR: Errno : 93 /* Attribute not found */ ENOATTR :: Platform_Error.ENOATTR /* Attribute not found */
/* Realtime, XSI STREAMS option errors */ /* Realtime, XSI STREAMS option errors */
EMULTIHOP: Errno : 94 /* Multihop attempted */ EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
ENOLINK: Errno : 95 /* Link has been severed */ ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
EPROTO: Errno : 96 /* Protocol error */ EPROTO :: Platform_Error.EPROTO /* Protocol error */
/* Robust mutexes */ /* Robust mutexes */
EOWNERDEAD: Errno : 97 /* Previous owner died */ EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Previous owner died */
ENOTRECOVERABLE: Errno : 98 /* State not recoverable */ ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE /* State not recoverable */
ELAST: Errno : 98 /* Must equal largest errno */ ELAST :: Platform_Error.ELAST /* Must equal largest Error */
/* end of errno */ /* end of Error */
O_RDONLY :: 0x000000000 O_RDONLY :: 0x000000000
O_WRONLY :: 0x000000001 O_WRONLY :: 0x000000001
@@ -268,13 +407,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -306,7 +445,7 @@ foreign libc {
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- @(link_name="__readdir_r30") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
@@ -334,154 +473,186 @@ foreign libc {
// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end. // NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end.
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__errno_location()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__errno_location()^)
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// We set a max of 1GB to keep alignment and to be safe. // We set a max of 1GB to keep alignment and to be safe.
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
s, err := _fstat(fd) file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
if err != ERROR_NONE { size = -1
return -1, err s := _fstat(fd) or_return
} size = s.size
return s.size, ERROR_NONE return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr) res := _unix_rename(old_path_cstr, new_path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr) res := _unix_unlink(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode) res := _unix_mkdir(path_cstr, mode)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr) res := _unix_rmdir(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -490,6 +661,7 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
is_file :: proc {is_file_path, is_file_handle} is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle} is_dir :: proc {is_dir_path, is_dir_handle}
@(require_results)
exists :: proc(path: string) -> bool { exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator) cpath := strings.clone_to_cstring(path, context.temp_allocator)
@@ -497,12 +669,13 @@ exists :: proc(path: string) -> bool {
return res == 0 return res == 0
} }
fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { @(require_results)
fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) {
result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg)) result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg))
if result < 0 { if result < 0 {
return 0, Errno(get_last_error()) return 0, get_last_error()
} }
return int(result), ERROR_NONE return int(result), nil
} }
// NOTE(bill): Uses startup to initialize it // NOTE(bill): Uses startup to initialize it
@@ -511,38 +684,34 @@ stdin: Handle = 0
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
s, err := _fstat(fd) last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _fstat(fd) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
s, err := _stat(name) last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _stat(name) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_lstat(cstr, &s) result := _unix_lstat(cstr, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -550,54 +719,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_fstat(fd, &s) result := _unix_fstat(fd, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE err = nil
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -607,8 +776,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -619,31 +788,28 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
return "", Errno{} return "", Error{}
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
buf: [MAX_PATH]byte buf: [MAX_PATH]byte
_, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) _ = fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) or_return
if err != ERROR_NONE { return strings.clone_from_cstring(cstring(&buf[0]))
return "", err
}
path := strings.clone_from_cstring(cstring(&buf[0]))
return path, err
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -654,26 +820,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask)) result := _unix_access(cstr, c.int(mask))
if result == -1 { if result == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
@@ -685,11 +852,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems. // an authoritative value for it across all systems.
@@ -701,7 +870,7 @@ get_current_directory :: proc() -> string {
if cwd != nil { if cwd != nil {
return string(cwd) return string(cwd)
} }
if Errno(get_last_error()) != ERANGE { if get_last_error() != ERANGE {
delete(buf) delete(buf)
return "" return ""
} }
@@ -710,14 +879,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr) res := _unix_chdir(cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -725,10 +894,12 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return int(_lwp_self()) return int(_lwp_self())
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
@@ -736,6 +907,7 @@ dlopen :: proc(filename: string, flags: int) -> rawptr {
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -749,10 +921,12 @@ dlclose :: proc(handle: rawptr) -> bool {
return _unix_dlclose(handle) == 0 return _unix_dlclose(handle) == 0
} }
@(require_results)
dlerror :: proc() -> string { dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -765,7 +939,7 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
count : int = 0 count : int = 0
count_size := size_of(count) count_size := size_of(count)
@@ -778,6 +952,7 @@ _processor_core_count :: proc() -> int {
return 1 return 1
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
+341 -204
View File
@@ -9,108 +9,205 @@ import "base:runtime"
Handle :: distinct i32 Handle :: distinct i32
Pid :: distinct i32 Pid :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno: 0 _Platform_Error :: enum i32 {
NONE = 0,
EPERM = 1,
ENOENT = 2,
ESRCH = 3,
EINTR = 4,
EIO = 5,
ENXIO = 6,
E2BIG = 7,
ENOEXEC = 8,
EBADF = 9,
ECHILD = 10,
EDEADLK = 11,
ENOMEM = 12,
EACCES = 13,
EFAULT = 14,
ENOTBLK = 15,
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,
EDOM = 33,
ERANGE = 34,
EAGAIN = 35,
EWOULDBLOCK = EAGAIN,
EINPROGRESS = 36,
EALREADY = 37,
ENOTSOCK = 38,
EDESTADDRREQ = 39,
EMSGSIZE = 40,
EPROTOTYPE = 41,
ENOPROTOOPT = 42,
EPROTONOSUPPORT = 43,
ESOCKTNOSUPPORT = 44,
EOPNOTSUPP = 45,
EPFNOSUPPORT = 46,
EAFNOSUPPORT = 47,
EADDRINUSE = 48,
EADDRNOTAVAIL = 49,
ENETDOWN = 50,
ENETUNREACH = 51,
ENETRESET = 52,
ECONNABORTED = 53,
ECONNRESET = 54,
ENOBUFS = 55,
EISCONN = 56,
ENOTCONN = 57,
ESHUTDOWN = 58,
ETOOMANYREFS = 59,
ETIMEDOUT = 60,
ECONNREFUSED = 61,
ELOOP = 62,
ENAMETOOLONG = 63,
EHOSTDOWN = 64,
EHOSTUNREACH = 65,
ENOTEMPTY = 66,
EPROCLIM = 67,
EUSERS = 68,
EDQUOT = 69,
ESTALE = 70,
EREMOTE = 71,
EBADRPC = 72,
ERPCMISMATCH = 73,
EPROGUNAVAIL = 74,
EPROGMISMATCH = 75,
EPROCUNAVAIL = 76,
ENOLCK = 77,
ENOSYS = 78,
EFTYPE = 79,
EAUTH = 80,
ENEEDAUTH = 81,
EIPSEC = 82,
ENOATTR = 83,
EILSEQ = 84,
ENOMEDIUM = 85,
EMEDIUMTYPE = 86,
EOVERFLOW = 87,
ECANCELED = 88,
EIDRM = 89,
ENOMSG = 90,
ENOTSUP = 91,
EBADMSG = 92,
ENOTRECOVERABLE = 93,
EOWNERDEAD = 94,
EPROTO = 95,
}
EPERM: Errno: 1 EPERM :: Platform_Error.EPERM
ENOENT: Errno: 2 ENOENT :: Platform_Error.ENOENT
ESRCH: Errno: 3 ESRCH :: Platform_Error.ESRCH
EINTR: Errno: 4 EINTR :: Platform_Error.EINTR
EIO: Errno: 5 EIO :: Platform_Error.EIO
ENXIO: Errno: 6 ENXIO :: Platform_Error.ENXIO
E2BIG: Errno: 7 E2BIG :: Platform_Error.E2BIG
ENOEXEC: Errno: 8 ENOEXEC :: Platform_Error.ENOEXEC
EBADF: Errno: 9 EBADF :: Platform_Error.EBADF
ECHILD: Errno: 10 ECHILD :: Platform_Error.ECHILD
EDEADLK: Errno: 11 EDEADLK :: Platform_Error.EDEADLK
ENOMEM: Errno: 12 ENOMEM :: Platform_Error.ENOMEM
EACCES: Errno: 13 EACCES :: Platform_Error.EACCES
EFAULT: Errno: 14 EFAULT :: Platform_Error.EFAULT
ENOTBLK: Errno: 15 ENOTBLK :: Platform_Error.ENOTBLK
EBUSY: Errno: 16 EBUSY :: Platform_Error.EBUSY
EEXIST: Errno: 17 EEXIST :: Platform_Error.EEXIST
EXDEV: Errno: 18 EXDEV :: Platform_Error.EXDEV
ENODEV: Errno: 19 ENODEV :: Platform_Error.ENODEV
ENOTDIR: Errno: 20 ENOTDIR :: Platform_Error.ENOTDIR
EISDIR: Errno: 21 EISDIR :: Platform_Error.EISDIR
EINVAL: Errno: 22 EINVAL :: Platform_Error.EINVAL
ENFILE: Errno: 23 ENFILE :: Platform_Error.ENFILE
EMFILE: Errno: 24 EMFILE :: Platform_Error.EMFILE
ENOTTY: Errno: 25 ENOTTY :: Platform_Error.ENOTTY
ETXTBSY: Errno: 26 ETXTBSY :: Platform_Error.ETXTBSY
EFBIG: Errno: 27 EFBIG :: Platform_Error.EFBIG
ENOSPC: Errno: 28 ENOSPC :: Platform_Error.ENOSPC
ESPIPE: Errno: 29 ESPIPE :: Platform_Error.ESPIPE
EROFS: Errno: 30 EROFS :: Platform_Error.EROFS
EMLINK: Errno: 31 EMLINK :: Platform_Error.EMLINK
EPIPE: Errno: 32 EPIPE :: Platform_Error.EPIPE
EDOM: Errno: 33 EDOM :: Platform_Error.EDOM
ERANGE: Errno: 34 ERANGE :: Platform_Error.ERANGE
EAGAIN: Errno: 35 EAGAIN :: Platform_Error.EAGAIN
EWOULDBLOCK: Errno: EAGAIN EWOULDBLOCK :: Platform_Error.EWOULDBLOCK
EINPROGRESS: Errno: 36 EINPROGRESS :: Platform_Error.EINPROGRESS
EALREADY: Errno: 37 EALREADY :: Platform_Error.EALREADY
ENOTSOCK: Errno: 38 ENOTSOCK :: Platform_Error.ENOTSOCK
EDESTADDRREQ: Errno: 39 EDESTADDRREQ :: Platform_Error.EDESTADDRREQ
EMSGSIZE: Errno: 40 EMSGSIZE :: Platform_Error.EMSGSIZE
EPROTOTYPE: Errno: 41 EPROTOTYPE :: Platform_Error.EPROTOTYPE
ENOPROTOOPT: Errno: 42 ENOPROTOOPT :: Platform_Error.ENOPROTOOPT
EPROTONOSUPPORT: Errno: 43 EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT
ESOCKTNOSUPPORT: Errno: 44 ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT
EOPNOTSUPP: Errno: 45 EOPNOTSUPP :: Platform_Error.EOPNOTSUPP
EPFNOSUPPORT: Errno: 46 EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT
EAFNOSUPPORT: Errno: 47 EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT
EADDRINUSE: Errno: 48 EADDRINUSE :: Platform_Error.EADDRINUSE
EADDRNOTAVAIL: Errno: 49 EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL
ENETDOWN: Errno: 50 ENETDOWN :: Platform_Error.ENETDOWN
ENETUNREACH: Errno: 51 ENETUNREACH :: Platform_Error.ENETUNREACH
ENETRESET: Errno: 52 ENETRESET :: Platform_Error.ENETRESET
ECONNABORTED: Errno: 53 ECONNABORTED :: Platform_Error.ECONNABORTED
ECONNRESET: Errno: 54 ECONNRESET :: Platform_Error.ECONNRESET
ENOBUFS: Errno: 55 ENOBUFS :: Platform_Error.ENOBUFS
EISCONN: Errno: 56 EISCONN :: Platform_Error.EISCONN
ENOTCONN: Errno: 57 ENOTCONN :: Platform_Error.ENOTCONN
ESHUTDOWN: Errno: 58 ESHUTDOWN :: Platform_Error.ESHUTDOWN
ETOOMANYREFS: Errno: 59 ETOOMANYREFS :: Platform_Error.ETOOMANYREFS
ETIMEDOUT: Errno: 60 ETIMEDOUT :: Platform_Error.ETIMEDOUT
ECONNREFUSED: Errno: 61 ECONNREFUSED :: Platform_Error.ECONNREFUSED
ELOOP: Errno: 62 ELOOP :: Platform_Error.ELOOP
ENAMETOOLONG: Errno: 63 ENAMETOOLONG :: Platform_Error.ENAMETOOLONG
EHOSTDOWN: Errno: 64 EHOSTDOWN :: Platform_Error.EHOSTDOWN
EHOSTUNREACH: Errno: 65 EHOSTUNREACH :: Platform_Error.EHOSTUNREACH
ENOTEMPTY: Errno: 66 ENOTEMPTY :: Platform_Error.ENOTEMPTY
EPROCLIM: Errno: 67 EPROCLIM :: Platform_Error.EPROCLIM
EUSERS: Errno: 68 EUSERS :: Platform_Error.EUSERS
EDQUOT: Errno: 69 EDQUOT :: Platform_Error.EDQUOT
ESTALE: Errno: 70 ESTALE :: Platform_Error.ESTALE
EREMOTE: Errno: 71 EREMOTE :: Platform_Error.EREMOTE
EBADRPC: Errno: 72 EBADRPC :: Platform_Error.EBADRPC
ERPCMISMATCH: Errno: 73 ERPCMISMATCH :: Platform_Error.ERPCMISMATCH
EPROGUNAVAIL: Errno: 74 EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL
EPROGMISMATCH: Errno: 75 EPROGMISMATCH :: Platform_Error.EPROGMISMATCH
EPROCUNAVAIL: Errno: 76 EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL
ENOLCK: Errno: 77 ENOLCK :: Platform_Error.ENOLCK
ENOSYS: Errno: 78 ENOSYS :: Platform_Error.ENOSYS
EFTYPE: Errno: 79 EFTYPE :: Platform_Error.EFTYPE
EAUTH: Errno: 80 EAUTH :: Platform_Error.EAUTH
ENEEDAUTH: Errno: 81 ENEEDAUTH :: Platform_Error.ENEEDAUTH
EIPSEC: Errno: 82 EIPSEC :: Platform_Error.EIPSEC
ENOATTR: Errno: 83 ENOATTR :: Platform_Error.ENOATTR
EILSEQ: Errno: 84 EILSEQ :: Platform_Error.EILSEQ
ENOMEDIUM: Errno: 85 ENOMEDIUM :: Platform_Error.ENOMEDIUM
EMEDIUMTYPE: Errno: 86 EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE
EOVERFLOW: Errno: 87 EOVERFLOW :: Platform_Error.EOVERFLOW
ECANCELED: Errno: 88 ECANCELED :: Platform_Error.ECANCELED
EIDRM: Errno: 89 EIDRM :: Platform_Error.EIDRM
ENOMSG: Errno: 90 ENOMSG :: Platform_Error.ENOMSG
ENOTSUP: Errno: 91 ENOTSUP :: Platform_Error.ENOTSUP
EBADMSG: Errno: 92 EBADMSG :: Platform_Error.EBADMSG
ENOTRECOVERABLE: Errno: 93 ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE
EOWNERDEAD: Errno: 94 EOWNERDEAD :: Platform_Error.EOWNERDEAD
EPROTO: Errno: 95 EPROTO :: Platform_Error.EPROTO
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
@@ -225,13 +322,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution S_ISGID :: 0o2000 // Set group id on execution
S_ISTXT :: 0o1000 // Sticky bit S_ISTXT :: 0o1000 // Sticky bit
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0x00 // Test for file existance F_OK :: 0x00 // Test for file existance
X_OK :: 0x01 // Test for execute permission X_OK :: 0x01 // Test for execute permission
@@ -291,38 +388,47 @@ foreign libc {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
} }
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__error()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__error()^)
} }
fork :: proc() -> (Pid, Errno) { @(require_results)
fork :: proc() -> (Pid, Error) {
pid := _unix_fork() pid := _unix_fork()
if pid == -1 { if pid == -1 {
return Pid(-1), Errno(get_last_error()) return Pid(-1), get_last_error()
} }
return Pid(pid), ERROR_NONE return Pid(pid), nil
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// If you read or write more than `SSIZE_MAX` bytes, OpenBSD returns `EINVAL`. // If you read or write more than `SSIZE_MAX` bytes, OpenBSD returns `EINVAL`.
@@ -333,124 +439,148 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
s, err := _fstat(fd) file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
if err != ERROR_NONE { size = -1
return -1, err s := _fstat(fd) or_return
} size = s.size
return s.size, ERROR_NONE return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr) res := _unix_rename(old_path_cstr, new_path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr) res := _unix_unlink(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode) res := _unix_mkdir(path_cstr, mode)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr) res := _unix_rmdir(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -469,26 +599,22 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {}
*/ */
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
s, err := _fstat(fd) last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _fstat(fd) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
s, err := _stat(name) last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _stat(name) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -496,13 +622,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_stat(cstr, &s) res := _unix_stat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -510,55 +636,55 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized // deliberately uninitialized
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_fstat(fd, &s) res := _unix_fstat(fd, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE err = nil
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -568,8 +694,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -579,23 +705,25 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
} }
// XXX OpenBSD // XXX OpenBSD
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
return "", Errno(ENOSYS) absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
return "", Error(ENOSYS)
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -606,25 +734,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask)) res := _unix_access(cstr, c.int(mask))
if res == -1 { if res == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator) path_str := strings.clone_to_cstring(key, context.temp_allocator)
@@ -635,11 +764,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
buf := make([dynamic]u8, MAX_PATH) buf := make([dynamic]u8, MAX_PATH)
for { for {
@@ -647,7 +778,7 @@ get_current_directory :: proc() -> string {
if cwd != nil { if cwd != nil {
return string(cwd) return string(cwd)
} }
if Errno(get_last_error()) != ERANGE { if get_last_error() != ERANGE {
delete(buf) delete(buf)
return "" return ""
} }
@@ -656,14 +787,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr) res := _unix_chdir(cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -671,16 +802,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return _unix_getthrid() return _unix_getthrid()
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags)) handle := _unix_dlopen(cstr, c.int(flags))
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -692,10 +826,12 @@ dlclose :: proc(handle: rawptr) -> bool {
assert(handle != nil) assert(handle != nil)
return _unix_dlclose(handle) == 0 return _unix_dlclose(handle) == 0
} }
@(require_results)
dlerror :: proc() -> string { dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -710,11 +846,12 @@ get_page_size :: proc() -> int {
_SC_NPROCESSORS_ONLN :: 503 _SC_NPROCESSORS_ONLN :: 503
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
return int(_sysconf(_SC_NPROCESSORS_ONLN)) return int(_sysconf(_SC_NPROCESSORS_ONLN))
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
+25 -18
View File
@@ -4,12 +4,10 @@ import "core:sys/wasm/wasi"
import "base:runtime" import "base:runtime"
Handle :: distinct i32 Handle :: distinct i32
Errno :: distinct i32 _Platform_Error :: wasi.errno_t
INVALID_HANDLE :: -1 INVALID_HANDLE :: -1
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
O_RDWR :: 0x00002 O_RDWR :: 0x00002
@@ -29,6 +27,7 @@ stderr: Handle = 2
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@(require_results)
_alloc_command_line_arguments :: proc() -> (args: []string) { _alloc_command_line_arguments :: proc() -> (args: []string) {
args = make([]string, len(runtime.args__)) args = make([]string, len(runtime.args__))
for &arg, i in args { for &arg, i in args {
@@ -93,8 +92,9 @@ init_preopens :: proc() {
preopens = dyn_preopens[:] preopens = dyn_preopens[:]
} }
@(require_results)
wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) { wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
@(require_results)
prefix_matches :: proc(prefix, path: string) -> bool { prefix_matches :: proc(prefix, path: string) -> bool {
// Empty is valid for any relative path. // Empty is valid for any relative path.
if len(prefix) == 0 && len(path) > 0 && path[0] != '/' { if len(prefix) == 0 && len(path) > 0 && path[0] != '/' {
@@ -148,23 +148,24 @@ wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.ciovec_t(data) iovs := wasi.ciovec_t(data)
n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.iovec_t(data) iovs := wasi.iovec_t(data)
n, err := wasi.fd_read(wasi.fd_t(fd), {iovs}) n, err := wasi.fd_read(wasi.fd_t(fd), {iovs})
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
iovs := wasi.ciovec_t(data) iovs := wasi.ciovec_t(data)
n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
iovs := wasi.iovec_t(data) iovs := wasi.iovec_t(data)
n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
@(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
oflags: wasi.oflags_t oflags: wasi.oflags_t
if mode & O_CREATE == O_CREATE { if mode & O_CREATE == O_CREATE {
@@ -201,30 +202,36 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
} }
fd, err := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags) fd, err := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags)
return Handle(fd), Errno(err) return Handle(fd), Platform_Error(err)
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Errno {
err := wasi.fd_close(wasi.fd_t(fd)) err := wasi.fd_close(wasi.fd_t(fd))
return Errno(err) return Platform_Error(err)
} }
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence)) n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence))
return i64(n), Errno(err) return i64(n), Platform_Error(err)
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return 0 return 0
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
return 1 return 1
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd)) file_size :: proc(fd: Handle) -> (size: i64, err: Errno) {
if err != nil { stat := wasi.fd_filestat_get(wasi.fd_t(fd)) or_return
return 0, Errno(err) size = i64(stat.size)
} return
return i64(stat.size), 0
} }
+106 -48
View File
@@ -7,7 +7,6 @@ import "base:intrinsics"
Handle :: distinct uintptr Handle :: distinct uintptr
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct int
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
@@ -27,70 +26,119 @@ O_SYNC :: 0x01000
O_ASYNC :: 0x02000 O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000 O_CLOEXEC :: 0x80000
_Platform_Error :: win32.System_Error
ERROR_NONE: Errno : 0 ERROR_FILE_NOT_FOUND :: _Platform_Error(2)
ERROR_FILE_NOT_FOUND: Errno : 2 ERROR_PATH_NOT_FOUND :: _Platform_Error(3)
ERROR_PATH_NOT_FOUND: Errno : 3 ERROR_ACCESS_DENIED :: _Platform_Error(5)
ERROR_ACCESS_DENIED: Errno : 5 ERROR_INVALID_HANDLE :: _Platform_Error(6)
ERROR_INVALID_HANDLE: Errno : 6 ERROR_NOT_ENOUGH_MEMORY :: _Platform_Error(8)
ERROR_NOT_ENOUGH_MEMORY: Errno : 8 ERROR_NO_MORE_FILES :: _Platform_Error(18)
ERROR_NO_MORE_FILES: Errno : 18 ERROR_HANDLE_EOF :: _Platform_Error(38)
ERROR_HANDLE_EOF: Errno : 38 ERROR_NETNAME_DELETED :: _Platform_Error(64)
ERROR_NETNAME_DELETED: Errno : 64 ERROR_FILE_EXISTS :: _Platform_Error(80)
ERROR_FILE_EXISTS: Errno : 80 ERROR_INVALID_PARAMETER :: _Platform_Error(87)
ERROR_INVALID_PARAMETER: Errno : 87 ERROR_BROKEN_PIPE :: _Platform_Error(109)
ERROR_BROKEN_PIPE: Errno : 109 ERROR_BUFFER_OVERFLOW :: _Platform_Error(111)
ERROR_BUFFER_OVERFLOW: Errno : 111 ERROR_INSUFFICIENT_BUFFER :: _Platform_Error(122)
ERROR_INSUFFICIENT_BUFFER: Errno : 122 ERROR_MOD_NOT_FOUND :: _Platform_Error(126)
ERROR_MOD_NOT_FOUND: Errno : 126 ERROR_PROC_NOT_FOUND :: _Platform_Error(127)
ERROR_PROC_NOT_FOUND: Errno : 127 ERROR_DIR_NOT_EMPTY :: _Platform_Error(145)
ERROR_DIR_NOT_EMPTY: Errno : 145 ERROR_ALREADY_EXISTS :: _Platform_Error(183)
ERROR_ALREADY_EXISTS: Errno : 183 ERROR_ENVVAR_NOT_FOUND :: _Platform_Error(203)
ERROR_ENVVAR_NOT_FOUND: Errno : 203 ERROR_MORE_DATA :: _Platform_Error(234)
ERROR_MORE_DATA: Errno : 234 ERROR_OPERATION_ABORTED :: _Platform_Error(995)
ERROR_OPERATION_ABORTED: Errno : 995 ERROR_IO_PENDING :: _Platform_Error(997)
ERROR_IO_PENDING: Errno : 997 ERROR_NOT_FOUND :: _Platform_Error(1168)
ERROR_NOT_FOUND: Errno : 1168 ERROR_PRIVILEGE_NOT_HELD :: _Platform_Error(1314)
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314 WSAEACCES :: _Platform_Error(10013)
WSAEACCES: Errno : 10013 WSAECONNRESET :: _Platform_Error(10054)
WSAECONNRESET: Errno : 10054
// Windows reserves errors >= 1<<29 for application use ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0 ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
// "Argv" arguments converted to Odin strings // "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@(require_results, no_instrumentation)
get_last_error :: proc "contextless" () -> Error {
err := win32.GetLastError()
if err == 0 {
return nil
}
switch err {
case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION:
return .Permission_Denied
case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS:
return .Exist
case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND:
return .Not_Exist
case win32.ERROR_NO_DATA:
return .Closed
case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT:
return .Timeout
case win32.ERROR_NOT_SUPPORTED:
return .Unsupported
case win32.ERROR_HANDLE_EOF:
return .EOF
case win32.ERROR_INVALID_HANDLE:
return .Invalid_File
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,
win32.ERROR_LOCK_FAILED,
win32.ERROR_ENVVAR_NOT_FOUND,
win32.ERROR_OPERATION_ABORTED,
win32.ERROR_IO_PENDING,
win32.ERROR_NO_UNICODE_TRANSLATION:
// fallthrough
}
return Platform_Error(err)
}
@(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
file_info: win32.BY_HANDLE_FILE_INFORMATION file_info: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) { if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime) lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime)
hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime) hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime)
return lo | hi << 32, ERROR_NONE return lo | hi << 32, nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
data: win32.WIN32_FILE_ATTRIBUTE_DATA data: win32.WIN32_FILE_ATTRIBUTE_DATA
wide_path := win32.utf8_to_wstring(name) wide_path := win32.utf8_to_wstring(name)
if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) { if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
l := File_Time(data.ftLastWriteTime.dwLowDateTime) l := File_Time(data.ftLastWriteTime.dwLowDateTime)
h := File_Time(data.ftLastWriteTime.dwHighDateTime) h := File_Time(data.ftLastWriteTime.dwHighDateTime)
return l | h << 32, ERROR_NONE return l | h << 32, nil
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -105,7 +153,7 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
length : win32.DWORD = 0 length : win32.DWORD = 0
result := win32.GetLogicalProcessorInformation(nil, &length) result := win32.GetLogicalProcessorInformation(nil, &length)
@@ -136,12 +184,14 @@ exit :: proc "contextless" (code: int) -> ! {
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return int(win32.GetCurrentThreadId()) return int(win32.GetCurrentThreadId())
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
arg_count: i32 arg_count: i32
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count) arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count)
@@ -175,44 +225,52 @@ _alloc_command_line_arguments :: proc() -> []string {
*/ */
WINDOWS_11_BUILD_CUTOFF :: 22_000 WINDOWS_11_BUILD_CUTOFF :: 22_000
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW { @(require_results)
get_windows_version_w :: proc "contextless" () -> win32.OSVERSIONINFOEXW {
osvi : win32.OSVERSIONINFOEXW osvi : win32.OSVERSIONINFOEXW
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW) osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
win32.RtlGetVersion(&osvi) win32.RtlGetVersion(&osvi)
return osvi return osvi
} }
is_windows_xp :: proc() -> bool { @(require_results)
is_windows_xp :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) return (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
} }
is_windows_vista :: proc() -> bool { @(require_results)
is_windows_vista :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
} }
is_windows_7 :: proc() -> bool { @(require_results)
is_windows_7 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
} }
is_windows_8 :: proc() -> bool { @(require_results)
is_windows_8 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
} }
is_windows_8_1 :: proc() -> bool { @(require_results)
is_windows_8_1 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3)
} }
is_windows_10 :: proc() -> bool { @(require_results)
is_windows_10 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < WINDOWS_11_BUILD_CUTOFF) return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < WINDOWS_11_BUILD_CUTOFF)
} }
is_windows_11 :: proc() -> bool { @(require_results)
is_windows_11 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF) return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
} }
+18 -38
View File
@@ -50,14 +50,14 @@ File_Info :: struct {
} }
*/ */
@private @(private, require_results)
_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time { _make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
return time.Time{ return time.Time{
_nsec = uft.nanoseconds + uft.seconds * 1_000_000_000, _nsec = uft.nanoseconds + uft.seconds * 1_000_000_000,
} }
} }
@private @(private)
_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) { _fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
fi.size = s.size fi.size = s.size
fi.mode = cast(File_Mode)s.mode fi.mode = cast(File_Mode)s.mode
@@ -71,7 +71,7 @@ _fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
} }
@private @(private, require_results)
path_base :: proc(path: string) -> string { path_base :: proc(path: string) -> string {
is_separator :: proc(c: byte) -> bool { is_separator :: proc(c: byte) -> bool {
return c == '/' return c == '/'
@@ -100,55 +100,35 @@ path_base :: proc(path: string) -> string {
} }
lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { @(require_results)
lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Error) {
context.allocator = allocator context.allocator = allocator
s: OS_Stat s := _lstat(name) or_return
s, err = _lstat(name)
if err != ERROR_NONE {
return fi, err
}
_fill_file_info_from_stat(&fi, s) _fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_relative(name) fi.fullpath = absolute_path_from_relative(name) or_return
if err != ERROR_NONE {
return
}
fi.name = path_base(fi.fullpath) fi.name = path_base(fi.fullpath)
return fi, ERROR_NONE return
} }
stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { @(require_results)
stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Error) {
context.allocator = allocator context.allocator = allocator
s: OS_Stat s := _stat(name) or_return
s, err = _stat(name)
if err != ERROR_NONE {
return fi, err
}
_fill_file_info_from_stat(&fi, s) _fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_relative(name) fi.fullpath = absolute_path_from_relative(name) or_return
if err != ERROR_NONE {
return
}
fi.name = path_base(fi.fullpath) fi.name = path_base(fi.fullpath)
return fi, ERROR_NONE return
} }
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) { @(require_results)
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Error) {
context.allocator = allocator context.allocator = allocator
s: OS_Stat s := _fstat(fd) or_return
s, err = _fstat(fd)
if err != ERROR_NONE {
return fi, err
}
_fill_file_info_from_stat(&fi, s) _fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_handle(fd) fi.fullpath = absolute_path_from_handle(fd) or_return
if err != ERROR_NONE {
return
}
fi.name = path_base(fi.fullpath) fi.name = path_base(fi.fullpath)
return fi, ERROR_NONE return
} }
+36 -36
View File
@@ -4,7 +4,7 @@ import "core:time"
import "base:runtime" import "base:runtime"
import win32 "core:sys/windows" import win32 "core:sys/windows"
@(private) @(private, require_results)
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) { full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
context.allocator = allocator context.allocator = allocator
@@ -19,10 +19,10 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
for { for {
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 { if n == 0 {
return "", Errno(win32.GetLastError()) return "", get_last_error()
} }
if n <= u32(len(buf)) { if n <= u32(len(buf)) {
return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE return win32.utf16_to_utf8(buf[:n], allocator) or_else "", nil
} }
resize(&buf, len(buf)*2) resize(&buf, len(buf)*2)
} }
@@ -30,7 +30,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
return return
} }
@(private) @(private, require_results)
_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) { _stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
if len(name) == 0 { if len(name) == 0 {
return {}, ERROR_PATH_NOT_FOUND return {}, ERROR_PATH_NOT_FOUND
@@ -54,7 +54,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
fd: win32.WIN32_FIND_DATAW fd: win32.WIN32_FIND_DATAW
sh := win32.FindFirstFileW(wname, &fd) sh := win32.FindFirstFileW(wname, &fd)
if sh == win32.INVALID_HANDLE_VALUE { if sh == win32.INVALID_HANDLE_VALUE {
e = Errno(win32.GetLastError()) e = get_last_error()
return return
} }
win32.FindClose(sh) win32.FindClose(sh)
@@ -64,7 +64,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE { if h == win32.INVALID_HANDLE_VALUE {
e = Errno(win32.GetLastError()) e = get_last_error()
return return
} }
defer win32.CloseHandle(h) defer win32.CloseHandle(h)
@@ -72,26 +72,29 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
} }
@(require_results)
lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
return _stat(name, attrs, allocator) return _stat(name, attrs, allocator)
} }
@(require_results)
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
return _stat(name, attrs, allocator) return _stat(name, attrs, allocator)
} }
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) { @(require_results)
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) {
if fd == 0 { if fd == 0 {
return {}, ERROR_INVALID_HANDLE err = ERROR_INVALID_HANDLE
} }
context.allocator = allocator context.allocator = allocator
path, err := cleanpath_from_handle(fd) path := cleanpath_from_handle(fd) or_return
if err != ERROR_NONE { defer if err != nil {
return {}, err delete(path)
} }
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
@@ -99,16 +102,16 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi.name = basename(path) fi.name = basename(path)
fi.mode |= file_type_mode(h) fi.mode |= file_type_mode(h)
errno = ERROR_NONE err = nil
case: case:
fi, errno = file_info_from_get_file_information_by_handle(path, h) fi = file_info_from_get_file_information_by_handle(path, h) or_return
} }
fi.fullpath = path fi.fullpath = path
return return
} }
@(private) @(private, require_results)
cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
buf := buf buf := buf
N := 0 N := 0
@@ -133,16 +136,13 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
return buf return buf
} }
@(private) @(private, require_results)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) { cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
buf, err := cleanpath_from_handle_u16(fd, context.temp_allocator) buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
if err != 0 { return win32.utf16_to_utf8(buf, context.allocator)
return "", err
}
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
} }
@(private) @(private, require_results)
cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) { cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
if fd == 0 { if fd == 0 {
return nil, ERROR_INVALID_HANDLE return nil, ERROR_INVALID_HANDLE
@@ -151,20 +151,20 @@ cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> (
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
if n == 0 { if n == 0 {
return nil, Errno(win32.GetLastError()) return nil, get_last_error()
} }
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0) buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
return buf[:buf_len], ERROR_NONE return buf[:buf_len], nil
} }
@(private) @(private, require_results)
cleanpath_from_buf :: proc(buf: []u16) -> string { cleanpath_from_buf :: proc(buf: []u16) -> string {
buf := buf buf := buf
buf = cleanpath_strip_prefix(buf) buf = cleanpath_strip_prefix(buf)
return win32.utf16_to_utf8(buf, context.allocator) or_else "" return win32.utf16_to_utf8(buf, context.allocator) or_else ""
} }
@(private) @(private, require_results)
basename :: proc(name: string) -> (base: string) { basename :: proc(name: string) -> (base: string) {
name := name name := name
if len(name) > 3 && name[:3] == `\\?` { if len(name) > 3 && name[:3] == `\\?` {
@@ -190,7 +190,7 @@ basename :: proc(name: string) -> (base: string) {
return name return name
} }
@(private) @(private, require_results)
file_type_mode :: proc(h: win32.HANDLE) -> File_Mode { file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
switch win32.GetFileType(h) { switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE: case win32.FILE_TYPE_PIPE:
@@ -202,7 +202,7 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
} }
@(private) @(private, require_results)
file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) { file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444 mode |= 0o444
@@ -239,7 +239,7 @@ windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
} }
@(private) @(private, require_results)
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) { file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -254,7 +254,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
return return
} }
@(private) @(private, require_results)
file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) { file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -269,20 +269,20 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
return return
} }
@(private) @(private, require_results)
file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) { file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
d: win32.BY_HANDLE_FILE_INFORMATION d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) { if !win32.GetFileInformationByHandle(h, &d) {
err := Errno(win32.GetLastError()) err := get_last_error()
return {}, err return {}, err
} }
ti: win32.FILE_ATTRIBUTE_TAG_INFO ti: win32.FILE_ATTRIBUTE_TAG_INFO
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
err := win32.GetLastError() err := get_last_error()
if err != u32(ERROR_INVALID_PARAMETER) { if err != ERROR_INVALID_PARAMETER {
return {}, Errno(err) return {}, err
} }
// Indicate this is a symlink on FAT file systems // Indicate this is a symlink on FAT file systems
ti.ReparseTag = 0 ti.ReparseTag = 0
@@ -299,5 +299,5 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
windows_set_file_info_times(&fi, &d) windows_set_file_info_times(&fi, &d)
return fi, ERROR_NONE return fi, nil
} }
+9 -26
View File
@@ -14,45 +14,37 @@ stream_from_handle :: proc(fd: Handle) -> io.Stream {
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
fd := Handle(uintptr(stream_data)) fd := Handle(uintptr(stream_data))
n_int: int n_int: int
os_err: Errno os_err: Error
switch mode { switch mode {
case .Close: case .Close:
close(fd) os_err = close(fd)
case .Flush: case .Flush:
when ODIN_OS == .Windows { os_err = flush(fd)
flush(fd)
} else {
// TOOD(bill): other operating systems
}
case .Read: case .Read:
n_int, os_err = read(fd, p) n_int, os_err = read(fd, p)
n = i64(n_int) n = i64(n_int)
if n == 0 && os_err == 0 { if n == 0 && os_err == nil {
err = .EOF err = .EOF
} }
case .Read_At: case .Read_At:
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) {
n_int, os_err = read_at(fd, p, offset) n_int, os_err = read_at(fd, p, offset)
n = i64(n_int) n = i64(n_int)
if n == 0 && os_err == 0 { if n == 0 && os_err == nil {
err = .EOF err = .EOF
} }
}
case .Write: case .Write:
n_int, os_err = write(fd, p) n_int, os_err = write(fd, p)
n = i64(n_int) n = i64(n_int)
if n == 0 && os_err == 0 { if n == 0 && os_err == nil {
err = .EOF err = .EOF
} }
case .Write_At: case .Write_At:
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) {
n_int, os_err = write_at(fd, p, offset) n_int, os_err = write_at(fd, p, offset)
n = i64(n_int) n = i64(n_int)
if n == 0 && os_err == 0 { if n == 0 && os_err == nil {
err = .EOF err = .EOF
} }
}
case .Seek: case .Seek:
n, os_err = seek(fd, offset, int(whence)) n, os_err = seek(fd, offset, int(whence))
case .Size: case .Size:
@@ -60,20 +52,11 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
case .Destroy: case .Destroy:
err = .Empty err = .Empty
case .Query: case .Query:
when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query})
} else {
return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
} }
}
if err == nil && os_err != 0 { if err == nil && os_err != nil {
when ODIN_OS == .Windows { err = error_to_io_error(os_err)
if os_err == ERROR_HANDLE_EOF {
return n, .EOF
}
}
err = .Unknown
} }
return return
} }
+2 -2
View File
@@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
d, derr := os.open(dir, os.O_RDONLY) d, derr := os.open(dir, os.O_RDONLY)
if derr != 0 { if derr != nil {
return return
} }
defer os.close(d) defer os.close(d)
@@ -280,7 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
file_info, ferr := os.fstat(d) file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info) defer os.file_info_delete(file_info)
if ferr != 0 { if ferr != nil {
return return
} }
if !file_info.is_dir { if !file_info.is_dir {
+5 -5
View File
@@ -52,7 +52,7 @@ is_abs :: proc(path: string) -> bool {
@(private) @(private)
temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) { temp_full_path :: proc(name: string) -> (path: string, err: os.Error) {
ta := context.temp_allocator ta := context.temp_allocator
name := name name := name
@@ -63,17 +63,17 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
p := win32.utf8_to_utf16(name, ta) p := win32.utf8_to_utf16(name, ta)
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
if n == 0 { if n == 0 {
return "", os.Errno(win32.GetLastError()) return "", os.get_last_error()
} }
buf := make([]u16, n, ta) buf := make([]u16, n, ta)
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 { if n == 0 {
delete(buf) delete(buf)
return "", os.Errno(win32.GetLastError()) return "", os.get_last_error()
} }
return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE return win32.utf16_to_utf8(buf[:n], ta)
} }
@@ -81,7 +81,7 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := temp_full_path(path) full_path, err := temp_full_path(path)
if err != 0 { if err != nil {
return "", false return "", false
} }
p := clean(full_path, allocator) p := clean(full_path, allocator)
+14 -21
View File
@@ -14,7 +14,7 @@ import "core:slice"
// The sole exception is if 'skip_dir' is returned as true: // The sole exception is if 'skip_dir' is returned as true:
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents // when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory // when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Error, user_data: rawptr) -> (err: os.Error, skip_dir: bool)
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root' // walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
// All errors that happen visiting files and directories are filtered by walk_proc // All errors that happen visiting files and directories are filtered by walk_proc
@@ -22,44 +22,44 @@ Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr)
// NOTE: Walking large directories can be inefficient due to the lexical sort // NOTE: Walking large directories can be inefficient due to the lexical sort
// NOTE: walk does not follow symbolic links // NOTE: walk does not follow symbolic links
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done // NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Errno { walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Error {
info, err := os.lstat(root, context.temp_allocator) info, err := os.lstat(root, context.temp_allocator)
defer os.file_info_delete(info, context.temp_allocator) defer os.file_info_delete(info, context.temp_allocator)
skip_dir: bool skip_dir: bool
if err != 0 { if err != nil {
err, skip_dir = walk_proc(info, err, user_data) err, skip_dir = walk_proc(info, err, user_data)
} else { } else {
err, skip_dir = _walk(info, walk_proc, user_data) err, skip_dir = _walk(info, walk_proc, user_data)
} }
return 0 if skip_dir else err return nil if skip_dir else err
} }
@(private) @(private)
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) { _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Error, skip_dir: bool) {
if !info.is_dir { if !info.is_dir {
if info.fullpath == "" && info.name == "" { if info.fullpath == "" && info.name == "" {
// ignore empty things // ignore empty things
return return
} }
return walk_proc(info, 0, user_data) return walk_proc(info, nil, user_data)
} }
fis: []os.File_Info fis: []os.File_Info
err1: os.Errno err1: os.Error
fis, err = read_dir(info.fullpath, context.temp_allocator) fis, err = read_dir(info.fullpath, context.temp_allocator)
defer os.file_info_slice_delete(fis, context.temp_allocator) defer os.file_info_slice_delete(fis, context.temp_allocator)
err1, skip_dir = walk_proc(info, err, user_data) err1, skip_dir = walk_proc(info, err, user_data)
if err != 0 || err1 != 0 || skip_dir { if err != nil || err1 != nil || skip_dir {
err = err1 err = err1
return return
} }
for fi in fis { for fi in fis {
err, skip_dir = _walk(fi, walk_proc, user_data) err, skip_dir = _walk(fi, walk_proc, user_data)
if err != 0 || skip_dir { if err != nil || skip_dir {
if !fi.is_dir || !skip_dir { if !fi.is_dir || !skip_dir {
return return
} }
@@ -70,19 +70,12 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (e
} }
@(private) @(private)
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) { read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> (fis: []os.File_Info, err: os.Error) {
f, err := os.open(dir_name, os.O_RDONLY) f := os.open(dir_name, os.O_RDONLY) or_return
if err != 0 { defer os.close(f)
return nil, err fis = os.read_dir(f, -1, allocator) or_return
}
fis: []os.File_Info
fis, err = os.read_dir(f, -1, allocator)
os.close(f)
if err != 0 {
return nil, err
}
slice.sort_by(fis, proc(a, b: os.File_Info) -> bool { slice.sort_by(fis, proc(a, b: os.File_Info) -> bool {
return a.name < b.name return a.name < b.name
}) })
return fis, 0 return
} }
+4 -2
View File
@@ -1,8 +1,10 @@
/* /*
import "base:runtime"
import "core:prof/spall" import "core:prof/spall"
import "core:sync"
spall_ctx: spall.Context spall_ctx: spall.Context
spall_buffer: spall.Buffer @(thread_local) spall_buffer: spall.Buffer
foo :: proc() { foo :: proc() {
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure) spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
@@ -13,7 +15,7 @@
defer spall.context_destroy(&spall_ctx) defer spall.context_destroy(&spall_ctx)
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE) buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
spall_buffer = spall.buffer_create(buffer_backing) spall_buffer = spall.buffer_create(buffer_backing, u32(sync.current_thread_id()))
defer spall.buffer_destroy(&spall_ctx, &spall_buffer) defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure) spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
+2 -2
View File
@@ -68,7 +68,7 @@ BUFFER_DEFAULT_SIZE :: 0x10_0000
context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok { context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok {
fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600) fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600)
if err != os.ERROR_NONE { if err != nil {
return return
} }
@@ -227,7 +227,7 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch
} }
@(no_instrumentation) @(no_instrumentation)
write :: proc "contextless" (fd: os.Handle, buf: []byte) -> (n: int, err: os.Errno) { write :: proc "contextless" (fd: os.Handle, buf: []byte) -> (n: int, err: os.Error) {
return _write(fd, buf) return _write(fd, buf)
} }
+3 -12
View File
@@ -10,21 +10,12 @@ import "core:sys/linux"
MAX_RW :: 0x7fffffff MAX_RW :: 0x7fffffff
@(no_instrumentation) @(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ { _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 {
return 0, os.ERROR_NONE
}
for n < len(data) { for n < len(data) {
chunk := data[:min(len(data), MAX_RW)] chunk := data[:min(len(data), MAX_RW)]
written, errno := linux.write(linux.Fd(fd), chunk) n += linux.write(linux.Fd(fd), chunk) or_return
if errno != .NONE {
return n, os.Errno(errno)
} }
n += written return
}
return n, os.ERROR_NONE
} }
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+4 -9
View File
@@ -22,29 +22,24 @@ foreign libc {
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 --- @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 ---
} }
@(no_instrumentation)
get_last_error :: proc "contextless" () -> int {
return int(__error()^)
}
MAX_RW :: 0x7fffffff MAX_RW :: 0x7fffffff
@(no_instrumentation) @(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ { _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 { if len(data) == 0 {
return 0, os.ERROR_NONE return 0, nil
} }
for n < len(data) { for n < len(data) {
chunk := data[:min(len(data), MAX_RW)] chunk := data[:min(len(data), MAX_RW)]
written := _unix_write(fd, raw_data(chunk), len(chunk)) written := _unix_write(fd, raw_data(chunk), len(chunk))
if written < 0 { if written < 0 {
return n, os.Errno(get_last_error()) return n, os.get_last_error()
} }
n += written n += written
} }
return n, os.ERROR_NONE return n, nil
} }
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+4 -5
View File
@@ -10,9 +10,9 @@ import win32 "core:sys/windows"
MAX_RW :: 1<<30 MAX_RW :: 1<<30
@(no_instrumentation) @(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ { _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 { if len(data) == 0 {
return 0, os.ERROR_NONE return 0, nil
} }
single_write_length: win32.DWORD single_write_length: win32.DWORD
@@ -25,12 +25,11 @@ _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Errno) #n
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil) e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
if single_write_length <= 0 || !e { if single_write_length <= 0 || !e {
err := os.Errno(win32.GetLastError()) return int(total_write), os.get_last_error()
return int(total_write), err
} }
total_write += i64(single_write_length) total_write += i64(single_write_length)
} }
return int(total_write), os.ERROR_NONE return int(total_write), nil
} }
@(no_instrumentation) @(no_instrumentation)
+58
View File
@@ -496,6 +496,64 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
return nil return nil
} }
Struct_Field_Count_Method :: enum {
Top_Level,
Using,
Recursive,
}
/*
Counts the number of fields in a struct
This procedure returns the number of fields in a struct, counting in one of three ways:
- .Top_Level: Only counts the top-level fields
- .Using: Same count as .Top_Level, and adds the field count of any `using s: Struct` it encounters (in addition to itself)
- .Recursive: The count of all top-level fields, plus the count of any child struct's fields, recursively
Inputs:
- T: The struct type
- method: The counting method
Returns:
- The `count`, enumerated using the `method`, which will be `0` if the type is not a struct
Example:
symbols_loaded, ok := dynlib.initialize_symbols(&game_api, "game.dll")
symbols_expected := reflect.struct_field_count(Game_Api) - API_PRIVATE_COUNT
if symbols_loaded != symbols_expected {
fmt.eprintf("Expected %v symbols, got %v", symbols_expected, symbols_loaded)
return
}
*/
@(require_results)
struct_field_count :: proc(T: typeid, method := Struct_Field_Count_Method.Top_Level) -> (count: int) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
switch method {
case .Top_Level:
return int(s.field_count)
case .Using:
count = int(s.field_count)
for type, i in s.types[:s.field_count] {
if s.usings[i] {
count += struct_field_count(type.id)
}
}
case .Recursive:
count = int(s.field_count)
for type in s.types[:s.field_count] {
count += struct_field_count(type.id)
}
case: return 0
}
}
return
}
@(require_results) @(require_results)
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) { struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
ti := runtime.type_info_base(type_info_of(T)) ti := runtime.type_info_base(type_info_of(T))
+14 -2
View File
@@ -74,8 +74,8 @@ shl_masked :: intrinsics.simd_shl_masked
shr_masked :: intrinsics.simd_shr_masked shr_masked :: intrinsics.simd_shr_masked
// Saturation Arithmetic // Saturation Arithmetic
add_sat :: intrinsics.simd_add_sat saturating_add :: intrinsics.simd_saturating_add
sub_sat :: intrinsics.simd_sub_sat saturating_sub :: intrinsics.simd_saturating_sub
bit_and :: intrinsics.simd_bit_and bit_and :: intrinsics.simd_bit_and
bit_or :: intrinsics.simd_bit_or bit_or :: intrinsics.simd_bit_or
@@ -102,6 +102,15 @@ lanes_le :: intrinsics.simd_lanes_le
lanes_gt :: intrinsics.simd_lanes_gt lanes_gt :: intrinsics.simd_lanes_gt
lanes_ge :: intrinsics.simd_lanes_ge lanes_ge :: intrinsics.simd_lanes_ge
// Gather and Scatter intrinsics
gather :: intrinsics.simd_gather
scatter :: intrinsics.simd_scatter
masked_load :: intrinsics.simd_masked_load
masked_store :: intrinsics.simd_masked_store
masked_expand_load :: intrinsics.simd_masked_expand_load
masked_compress_store :: intrinsics.simd_masked_compress_store
// extract :: proc(a: #simd[N]T, idx: uint) -> T // extract :: proc(a: #simd[N]T, idx: uint) -> T
extract :: intrinsics.simd_extract extract :: intrinsics.simd_extract
// replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T // replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T
@@ -115,6 +124,9 @@ reduce_and :: intrinsics.simd_reduce_and
reduce_or :: intrinsics.simd_reduce_or reduce_or :: intrinsics.simd_reduce_or
reduce_xor :: intrinsics.simd_reduce_xor reduce_xor :: intrinsics.simd_reduce_xor
reduce_any :: intrinsics.simd_reduce_any
reduce_all :: intrinsics.simd_reduce_all
// swizzle :: proc(a: #simd[N]T, indices: ..int) -> #simd[len(indices)]T // swizzle :: proc(a: #simd[N]T, indices: ..int) -> #simd[len(indices)]T
swizzle :: builtin.swizzle swizzle :: builtin.swizzle
+8 -8
View File
@@ -39,19 +39,19 @@ _mm_add_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_adds_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_adds_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.add_sat(transmute(i8x16)a, transmute(i8x16)b) return transmute(__m128i)simd.saturating_add(transmute(i8x16)a, transmute(i8x16)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_adds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_adds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.add_sat(transmute(i16x8)a, transmute(i16x8)b) return transmute(__m128i)simd.saturating_add(transmute(i16x8)a, transmute(i16x8)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_adds_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_adds_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.add_sat(transmute(u8x16)a, transmute(u8x16)b) return transmute(__m128i)simd.saturating_add(transmute(u8x16)a, transmute(u8x16)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_adds_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_adds_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.add_sat(transmute(u16x8)a, transmute(u16x8)b) return transmute(__m128i)simd.saturating_add(transmute(u16x8)a, transmute(u16x8)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_avg_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_avg_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
@@ -122,19 +122,19 @@ _mm_sub_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_subs_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_subs_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.sub_sat(transmute(i8x16)a, transmute(i8x16)b) return transmute(__m128i)simd.saturating_sub(transmute(i8x16)a, transmute(i8x16)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_subs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_subs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.sub_sat(transmute(i16x8)a, transmute(i16x8)b) return transmute(__m128i)simd.saturating_sub(transmute(i16x8)a, transmute(i16x8)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_subs_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_subs_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.sub_sat(transmute(u8x16)a, transmute(u8x16)b) return transmute(__m128i)simd.saturating_sub(transmute(u8x16)a, transmute(u8x16)b)
} }
@(require_results, enable_target_feature="sse2") @(require_results, enable_target_feature="sse2")
_mm_subs_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { _mm_subs_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)simd.sub_sat(transmute(u16x8)a, transmute(u16x8)b) return transmute(__m128i)simd.saturating_sub(transmute(u16x8)a, transmute(u16x8)b)
} }
+411 -3
View File
@@ -2,44 +2,452 @@ package sync
import "base:intrinsics" import "base:intrinsics"
/*
This procedure may lower CPU consumption or yield to a hyperthreaded twin
processor. It's exact function is architecture specific, but the intent is to
say that you're not doing much on a CPU.
*/
cpu_relax :: intrinsics.cpu_relax cpu_relax :: intrinsics.cpu_relax
/* /*
Atomic_Memory_Order :: enum { Describes memory ordering for an atomic operation.
Modern CPU's contain multiple cores and caches specific to those cores. When a
core performs a write to memory, the value is written to cache first. The issue
is that a core doesn't typically see what's inside the caches of other cores.
In order to make operations consistent CPU's implement mechanisms that
synchronize memory operations across cores by asking other cores or by
pushing data about writes to other cores.
Due to how these algorithms are implemented, the stores and loads performed by
one core may seem to happen in a different order to another core. It also may
happen that a core reorders stores and loads (independent of how compiler put
them into the machine code). This can cause issues when trying to synchronize
multiple memory locations between two cores. Which is why CPU's allow for
stronger memory ordering guarantees if certain instructions or instruction
variants are used.
In Odin there are 5 different memory ordering guaranties that can be provided
to an atomic operation:
- `Relaxed`: The memory access (load or store) is unordered with respect to
other memory accesses. This can be used to implement an atomic counter.
Multiple threads access a single variable, but it doesn't matter when
exactly it gets incremented, because it will become eventually consistent.
- `Consume`: No loads or stores dependent on a memory location can be
reordered before a load with consume memory order. If other threads released
the same memory, it becomes visible.
- `Acquire`: No loads or stores on a memory location can be reordered before a
load of that memory location with acquire memory ordering. If other threads
release the same memory, it becomes visible.
- `Release`: No loads or stores on a memory location can be reordered after a
store of that memory location with release memory ordering. All threads that
acquire the same memory location will see all writes done by the current
thread.
- `Acq_Rel`: Acquire-release memory ordering: combines acquire and release
memory orderings in the same operation.
- `Seq_Cst`: Sequential consistency. The strongest memory ordering. A load will
always be an acquire operation, a store will always be a release operation,
and in addition to that all threads observe the same order of writes.
Non-explicit atomics will always be sequentially consistent.
Atomic_Memory_Order :: enum {
Relaxed = 0, // Unordered Relaxed = 0, // Unordered
Consume = 1, // Monotonic Consume = 1, // Monotonic
Acquire = 2, Acquire = 2,
Release = 3, Release = 3,
Acq_Rel = 4, Acq_Rel = 4,
Seq_Cst = 5, Seq_Cst = 5,
} }
**Note(i386, x64)**: x86 has a very strong memory model by default. It
guarantees that all writes are ordered, stores and loads aren't reordered. In
a sense, all operations are at least acquire and release operations. If `lock`
prefix is used, all operations are sequentially consistent. If you use explicit
atomics, make sure you have the correct atomic memory order, because bugs likely
will not show up in x86, but may show up on e.g. arm. More on x86 memory
ordering can be found
[[here; https://www.cs.cmu.edu/~410-f10/doc/Intel_Reordering_318147.pdf]]
*/ */
Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order
/*
Establish memory ordering.
This procedure establishes memory ordering, without an associated atomic
operation.
*/
atomic_thread_fence :: intrinsics.atomic_thread_fence atomic_thread_fence :: intrinsics.atomic_thread_fence
/*
Establish memory ordering between a current thread and a signal handler.
This procedure establishes memory ordering between a thread and a signal
handler, that run on the same thread, without an associated atomic operation.
This procedure is equivalent to `atomic_thread_fence`, except it doesn't
issue any CPU instructions for memory ordering.
*/
atomic_signal_fence :: intrinsics.atomic_signal_fence atomic_signal_fence :: intrinsics.atomic_signal_fence
/*
Atomically store a value into memory.
This procedure stores a value to a memory location in such a way that no other
thread is able to see partial reads. This operation is sequentially-consistent.
*/
atomic_store :: intrinsics.atomic_store atomic_store :: intrinsics.atomic_store
/*
Atomically store a value into memory with explicit memory ordering.
This procedure stores a value to a memory location in such a way that no other
thread is able to see partial reads. The memory ordering of this operation is
as specified by the `order` parameter.
*/
atomic_store_explicit :: intrinsics.atomic_store_explicit atomic_store_explicit :: intrinsics.atomic_store_explicit
/*
Atomically load a value from memory.
This procedure loads a value from a memory location in such a way that the
received value is not a partial read. The memory ordering of this operation is
sequentially-consistent.
*/
atomic_load :: intrinsics.atomic_load atomic_load :: intrinsics.atomic_load
/*
Atomically load a value from memory with explicit memory ordering.
This procedure loads a value from a memory location in such a way that the
received value is not a partial read. The memory ordering of this operation
is as specified by the `order` parameter.
*/
atomic_load_explicit :: intrinsics.atomic_load_explicit atomic_load_explicit :: intrinsics.atomic_load_explicit
/*
Atomically add a value to the value stored in memory.
This procedure loads a value from memory, adds the specified value to it, and
stores it back as an atomic operation. This operation is an atomic equivalent
of the following:
dst^ += val
The memory ordering of this operation is sequentially-consistent.
*/
atomic_add :: intrinsics.atomic_add atomic_add :: intrinsics.atomic_add
/*
Atomically add a value to the value stored in memory.
This procedure loads a value from memory, adds the specified value to it, and
stores it back as an atomic operation. This operation is an atomic equivalent
of the following:
dst^ += val
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_add_explicit :: intrinsics.atomic_add_explicit atomic_add_explicit :: intrinsics.atomic_add_explicit
/*
Atomically subtract a value from the value stored in memory.
This procedure loads a value from memory, subtracts the specified value from it,
and stores the result back as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ -= val
The memory ordering of this operation is sequentially-consistent.
*/
atomic_sub :: intrinsics.atomic_sub atomic_sub :: intrinsics.atomic_sub
/*
Atomically subtract a value from the value stored in memory.
This procedure loads a value from memory, subtracts the specified value from it,
and stores the result back as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ -= val
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_sub_explicit :: intrinsics.atomic_sub_explicit atomic_sub_explicit :: intrinsics.atomic_sub_explicit
/*
Atomically replace the memory location with the result of AND operation with
the specified value.
This procedure loads a value from memory, calculates the result of AND operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ &= val
The memory ordering of this operation is sequentially-consistent.
*/
atomic_and :: intrinsics.atomic_and atomic_and :: intrinsics.atomic_and
/*
Atomically replace the memory location with the result of AND operation with
the specified value.
This procedure loads a value from memory, calculates the result of AND operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ &= val
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_and_explicit :: intrinsics.atomic_and_explicit atomic_and_explicit :: intrinsics.atomic_and_explicit
/*
Atomically replace the memory location with the result of NAND operation with
the specified value.
This procedure loads a value from memory, calculates the result of NAND operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ = ~(dst^ & val)
The memory ordering of this operation is sequentially-consistent.
*/
atomic_nand :: intrinsics.atomic_nand atomic_nand :: intrinsics.atomic_nand
/*
Atomically replace the memory location with the result of NAND operation with
the specified value.
This procedure loads a value from memory, calculates the result of NAND operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ = ~(dst^ & val)
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_nand_explicit :: intrinsics.atomic_nand_explicit atomic_nand_explicit :: intrinsics.atomic_nand_explicit
/*
Atomically replace the memory location with the result of OR operation with
the specified value.
This procedure loads a value from memory, calculates the result of OR operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ |= val
The memory ordering of this operation is sequentially-consistent.
*/
atomic_or :: intrinsics.atomic_or atomic_or :: intrinsics.atomic_or
/*
Atomically replace the memory location with the result of OR operation with
the specified value.
This procedure loads a value from memory, calculates the result of OR operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ |= val
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_or_explicit :: intrinsics.atomic_or_explicit atomic_or_explicit :: intrinsics.atomic_or_explicit
/*
Atomically replace the memory location with the result of XOR operation with
the specified value.
This procedure loads a value from memory, calculates the result of XOR operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ ~= val
The memory ordering of this operation is sequentially-consistent.
*/
atomic_xor :: intrinsics.atomic_xor atomic_xor :: intrinsics.atomic_xor
/*
Atomically replace the memory location with the result of XOR operation with
the specified value.
This procedure loads a value from memory, calculates the result of XOR operation
between the loaded value and the specified value, and stores it back into the
same memory location as an atomic operation. This operation is an atomic
equivalent of the following:
dst^ ~= val
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_xor_explicit :: intrinsics.atomic_xor_explicit atomic_xor_explicit :: intrinsics.atomic_xor_explicit
/*
Atomically exchange the value in a memory location, with the specified value.
This procedure loads a value from the specified memory location, and stores the
specified value into that memory location. Then the loaded value is returned,
all done in a single atomic operation. This operation is an atomic equivalent
of the following:
tmp := dst^
dst^ = val
return tmp
The memory ordering of this operation is sequentially-consistent.
*/
atomic_exchange :: intrinsics.atomic_exchange atomic_exchange :: intrinsics.atomic_exchange
/*
Atomically exchange the value in a memory location, with the specified value.
This procedure loads a value from the specified memory location, and stores the
specified value into that memory location. Then the loaded value is returned,
all done in a single atomic operation. This operation is an atomic equivalent
of the following:
tmp := dst^
dst^ = val
return tmp
The memory ordering of this operation is as specified by the `order` parameter.
*/
atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
// Returns value and optional ok boolean /*
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the `dst` parameter is equal
to `old`, and if they are, it stores the value `new` into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether `old` was
equal to `new`.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^
if old_dst == old {
dst^ = new
return old_dst, true
} else {
return old_dst, false
}
The strong version of compare exchange always returns true, when the returned
old value stored in location pointed to by `dst` and the `old` parameter are
equal.
Atomic compare exchange has two memory orderings: One is for the
read-modify-write operation, if the comparison succeeds, and the other is for
the load operation, if the comparison fails. The memory ordering for both of
of these operations is sequentially-consistent.
*/
atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
/*
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the `dst` parameter is equal
to `old`, and if they are, it stores the value `new` into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether `old` was
equal to `new`.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^
if old_dst == old {
dst^ = new
return old_dst, true
} else {
return old_dst, false
}
The strong version of compare exchange always returns true, when the returned
old value stored in location pointed to by `dst` and the `old` parameter are
equal.
Atomic compare exchange has two memory orderings: One is for the
read-modify-write operation, if the comparison succeeds, and the other is for
the load operation, if the comparison fails. The memory ordering for these
operations is as specified by `success` and `failure` parameters respectively.
*/
atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
/*
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the `dst` parameter is equal
to `old`, and if they are, it stores the value `new` into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether `old` was
equal to `new`.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^
if old_dst == old {
// may return false here
dst^ = new
return old_dst, true
} else {
return old_dst, false
}
The weak version of compare exchange may return false, even if `dst^ == old`.
On some platforms running weak compare exchange in a loop is faster than a
strong version.
Atomic compare exchange has two memory orderings: One is for the
read-modify-write operation, if the comparison succeeds, and the other is for
the load operation, if the comparison fails. The memory ordering for both
of these operations is sequentially-consistent.
*/
atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
/*
Atomically compare and exchange the value with a memory location.
This procedure checks if the value pointed to by the `dst` parameter is equal
to `old`, and if they are, it stores the value `new` into the memory location,
all done in a single atomic operation. This procedure returns the old value
stored in a memory location and a boolean value signifying whether `old` was
equal to `new`.
This procedure is an atomic equivalent of the following operation:
old_dst := dst^
if old_dst == old {
// may return false here
dst^ = new
return old_dst, true
} else {
return old_dst, false
}
The weak version of compare exchange may return false, even if `dst^ == old`.
On some platforms running weak compare exchange in a loop is faster than a
strong version.
Atomic compare exchange has two memory orderings: One is for the
read-modify-write operation, if the comparison succeeds, and the other is for
the load operation, if the comparison fails. The memory ordering for these
operations is as specified by the `success` and `failure` parameters
respectively.
*/
atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit
+21
View File
@@ -0,0 +1,21 @@
/*
Synchronization primitives
This package implements various synchronization primitives that can be used to
synchronize threads' access to shared memory.
To limit or control the threads' access to shared memory typically the
following approaches are used:
* Locks
* Lock-free
When using locks, sections of the code that access shared memory (also known as
**critical sections**) are guarded by locks, allowing limited access to threads
and blocking the execution of any other threads.
In lock-free programming the data itself is organized in such a way that threads
don't intervene much. It can be done via segmenting the data between threads,
and/or by using atomic operations.
*/
package sync
+373 -74
View File
@@ -4,15 +4,41 @@ import "core:time"
import vg "core:sys/valgrind" import vg "core:sys/valgrind"
_ :: vg _ :: vg
// A Wait_Group waits for a collection of threads to finish /*
// Wait group.
// A Wait_Group must not be copied after first use
Wait group is a synchronization primitive used by the waiting thread to wait,
until a all working threads finish work.
The waiting thread first sets the number of working threads it will expect to
wait for using `wait_group_add` call, and start waiting using `wait_group_wait`
call. When worker threads complete their work, each of them will call
`wait_group_done`, and after all working threads have called this procedure,
the waiting thread will resume execution.
For the purpose of keeping track whether all working threads have finished their
work, the wait group keeps an internal atomic counter. Initially, the waiting
thread might set it to a certain non-zero amount. When each working thread
completes the work, the internal counter is atomically decremented until it
reaches zero. When it reaches zero, the waiting thread is unblocked. The counter
is not allowed to become negative.
**Note**: Just like any synchronization primitives, a wait group cannot be
copied after first use. See documentation for `Mutex` or `Cond`.
*/
Wait_Group :: struct #no_copy { Wait_Group :: struct #no_copy {
counter: int, counter: int,
mutex: Mutex, mutex: Mutex,
cond: Cond, cond: Cond,
} }
/*
Increment an internal counter of a wait group.
This procedure atomicaly increments a number to the specified wait group's
internal counter by a specified amount. This operation can be done on any
thread.
*/
wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) { wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {
if delta == 0 { if delta == 0 {
return return
@@ -32,10 +58,23 @@ wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {
} }
} }
/*
Signal work done by a thread in a wait group.
This procedure decrements the internal counter of the specified wait group and
wakes up the waiting thread. Once the internal counter reaches zero, the waiting
thread resumes execution.
*/
wait_group_done :: proc "contextless" (wg: ^Wait_Group) { wait_group_done :: proc "contextless" (wg: ^Wait_Group) {
wait_group_add(wg, -1) wait_group_add(wg, -1)
} }
/*
Wait for all worker threads in the wait group.
This procedure blocks the execution of the current thread, until the specified
wait group's internal counter reaches zero.
*/
wait_group_wait :: proc "contextless" (wg: ^Wait_Group) { wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {
guard(&wg.mutex) guard(&wg.mutex)
@@ -47,6 +86,14 @@ wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {
} }
} }
/*
Wait for all worker threads in the wait group, or until timeout is reached.
This procedure blocks the execution of the current thread, until the specified
wait group's internal counter reaches zero, or until the timeout is reached.
This procedure returns `false`, if the timeout was reached, `true` otherwise.
*/
wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: time.Duration) -> bool { wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: time.Duration) -> bool {
if duration <= 0 { if duration <= 0 {
return false return false
@@ -64,28 +111,33 @@ wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: t
return true return true
} }
/* /*
A barrier enabling multiple threads to synchronize the beginning of some computation Barrier.
Example: A barrier is a synchronization primitive enabling multiple threads to
package example synchronize the beginning of some computation.
import "core:fmt" When `barrier_wait` procedure is called by any thread, that thread will block
import "core:sync" the execution, until all threads associated with the barrier reach the same
import "core:thread" point of execution and also call `barrier_wait`.
barrier := &sync.Barrier{} when barrier is initialized, a `thread_count` parameter is passed, signifying
the amount of participant threads of the barrier. The barrier also keeps track
of an internal atomic counter. When a thread calls `barrier_wait`, the internal
counter is incremented. When the internal counter reaches `thread_count`, it is
reset and all threads waiting on the barrier are unblocked.
main :: proc "contextless" () { This type of synchronization primitive can be used to synchronize "staged"
fmt.println("Start") workloads, where the workload is split into stages, and until all threads have
completed the previous threads, no thread is allowed to start work on the next
stage. In this case, after each stage, a `barrier_wait` shall be inserted in the
thread procedure.
**Example**:
THREAD_COUNT :: 4 THREAD_COUNT :: 4
threads: [THREAD_COUNT]^thread.Thread threads: [THREAD_COUNT]^thread.Thread
sync.barrier_init(barrier, THREAD_COUNT) sync.barrier_init(barrier, THREAD_COUNT)
for _, i in threads { for _, i in threads {
threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
// Same messages will be printed together but without any interleaving // Same messages will be printed together but without any interleaving
@@ -94,11 +146,8 @@ Example:
fmt.println("Off their marks they go!") fmt.println("Off their marks they go!")
}) })
} }
for t in threads { for t in threads {
thread.destroy(t) // join and free thread thread.destroy(t)
}
fmt.println("Finished")
} }
*/ */
Barrier :: struct #no_copy { Barrier :: struct #no_copy {
@@ -109,6 +158,13 @@ Barrier :: struct #no_copy {
thread_count: int, thread_count: int,
} }
/*
Initialize a barrier.
This procedure initializes the barrier for the specified amount of participant
threads.
*/
barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) { barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
when ODIN_VALGRIND_SUPPORT { when ODIN_VALGRIND_SUPPORT {
vg.helgrind_barrier_resize_pre(b, uint(thread_count)) vg.helgrind_barrier_resize_pre(b, uint(thread_count))
@@ -118,8 +174,13 @@ barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
b.thread_count = thread_count b.thread_count = thread_count
} }
// Block the current thread until all threads have rendezvoused /*
// Barrier can be reused after all threads rendezvoused once, and can be used continuously Block the current thread until all threads have rendezvoused.
This procedure blocks the execution of the current thread, until all threads
have reached the same point in the execution of the thread proc. Multiple calls
to `barrier_wait` are allowed within the thread procedure.
*/
barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) { barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
when ODIN_VALGRIND_SUPPORT { when ODIN_VALGRIND_SUPPORT {
vg.helgrind_barrier_wait_pre(b) vg.helgrind_barrier_wait_pre(b)
@@ -140,15 +201,31 @@ barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
return true return true
} }
/*
Auto-reset event.
Represents a thread synchronization primitive that, when signalled, releases one
single waiting thread and then resets automatically to a state where it can be
signalled again.
When a thread calls `auto_reset_event_wait`, it's execution will be blocked,
until the event is signalled by another thread. The call to
`auto_reset_event_signal` wakes up exactly one thread waiting for the event.
*/
Auto_Reset_Event :: struct #no_copy { Auto_Reset_Event :: struct #no_copy {
// status == 0: Event is reset and no threads are waiting // status == 0: Event is reset and no threads are waiting
// status == 1: Event is signaled // status == 1: Event is signalled
// status == -N: Event is reset and N threads are waiting // status == -N: Event is reset and N threads are waiting
status: i32, status: i32,
sema: Sema, sema: Sema,
} }
/*
Signal an auto-reset event.
This procedure signals an auto-reset event, waking up exactly one waiting
thread.
*/
auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) { auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) {
old_status := atomic_load_explicit(&e.status, .Relaxed) old_status := atomic_load_explicit(&e.status, .Relaxed)
for { for {
@@ -163,6 +240,12 @@ auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) {
} }
} }
/*
Wait on an auto-reset event.
This procedure blocks the execution of the current thread, until the event is
signalled by another thread.
*/
auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) { auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {
old_status := atomic_sub_explicit(&e.status, 1, .Acquire) old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
if old_status < 1 { if old_status < 1 {
@@ -170,13 +253,35 @@ auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {
} }
} }
/*
Ticket lock.
A ticket lock is a mutual exclusion lock that uses "tickets" to control which
thread is allowed into a critical section.
This synchronization primitive works just like spinlock, except that it implements
a "fairness" guarantee, making sure that each thread gets a roughly equal amount
of entries into the critical section.
This type of synchronization primitive is applicable for short critical sections
in low-contention systems, as it uses a spinlock under the hood.
*/
Ticket_Mutex :: struct #no_copy { Ticket_Mutex :: struct #no_copy {
ticket: uint, ticket: uint,
serving: uint, serving: uint,
} }
/*
Acquire a lock on a ticket mutex.
This procedure acquires a lock on a ticket mutex. If the ticket mutex is held
by another thread, this procedure also blocks the execution until the lock
can be acquired.
Once the lock is acquired, any thread calling `ticket_mutex_lock` will be
blocked from entering any critical sections associated with the same ticket
mutex, until the lock is released.
*/
ticket_mutex_lock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) { ticket_mutex_lock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed) ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
for ticket != atomic_load_explicit(&m.serving, .Acquire) { for ticket != atomic_load_explicit(&m.serving, .Acquire) {
@@ -184,44 +289,147 @@ ticket_mutex_lock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
} }
} }
/*
Release a lock on a ticket mutex.
This procedure releases the lock on a ticket mutex. If any of the threads are
waiting to acquire the lock, exactly one of those threads is unblocked and
allowed into the critical section.
*/
ticket_mutex_unlock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) { ticket_mutex_unlock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
atomic_add_explicit(&m.serving, 1, .Relaxed) atomic_add_explicit(&m.serving, 1, .Relaxed)
} }
/*
Guard the current scope with a lock on a ticket mutex.
This procedure acquires a lock on a ticket mutex. The lock is automatically
released at the end of callee's scope. If the mutex was already locked, this
procedure also blocks until the lock can be acquired.
When a lock has been acquired, all threads attempting to acquire a lock will be
blocked from entering any critical sections associated with the ticket mutex,
until the lock is released.
This procedure always returns `true`. This makes it easy to define a critical
section by putting the function inside the `if` statement.
**Example**:
if ticket_mutex_guard(&m) {
...
}
*/
@(deferred_in=ticket_mutex_unlock) @(deferred_in=ticket_mutex_unlock)
ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool { ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {
ticket_mutex_lock(m) ticket_mutex_lock(m)
return true return true
} }
/*
Benaphore.
A benaphore is a combination of an atomic variable and a semaphore that can
improve locking efficiency in a no-contention system. Acquiring a benaphore
lock doesn't call into an internal semaphore, if no other thread in a middle of
a critical section.
Once a lock on a benaphore is acquired by a thread, no other thread is allowed
into any critical sections, associted with the same benaphore, until the lock
is released.
*/
Benaphore :: struct #no_copy { Benaphore :: struct #no_copy {
counter: i32, counter: i32,
sema: Sema, sema: Sema,
} }
/*
Acquire a lock on a benaphore.
This procedure acquires a lock on the specified benaphore. If the lock on a
benaphore is already held, this procedure also blocks the execution of the
current thread, until the lock could be acquired.
Once a lock is acquired, all threads attempting to take a lock will be blocked
from entering any critical sections associated with the same benaphore, until
until the lock is released.
*/
benaphore_lock :: proc "contextless" (b: ^Benaphore) { benaphore_lock :: proc "contextless" (b: ^Benaphore) {
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
sema_wait(&b.sema) sema_wait(&b.sema)
} }
} }
/*
Try to acquire a lock on a benaphore.
This procedure tries to acquire a lock on the specified benaphore. If it was
already locked, then the returned value is `false`, otherwise the lock is
acquired and the procedure returns `true`.
If the lock is acquired, all threads that attempt to acquire a lock will be
blocked from entering any critical sections associated with the same benaphore,
until the lock is released.
*/
benaphore_try_lock :: proc "contextless" (b: ^Benaphore) -> bool { benaphore_try_lock :: proc "contextless" (b: ^Benaphore) -> bool {
v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire) v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
return v == 0 return v == 0
} }
/*
Release a lock on a benaphore.
This procedure releases a lock on the specified benaphore. If any of the threads
are waiting on the lock, exactly one thread is allowed into a critical section
associated with the same banaphore.
*/
benaphore_unlock :: proc "contextless" (b: ^Benaphore) { benaphore_unlock :: proc "contextless" (b: ^Benaphore) {
if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
sema_post(&b.sema) sema_post(&b.sema)
} }
} }
/*
Guard the current scope with a lock on a benaphore.
This procedure acquires a lock on a benaphore. The lock is automatically
released at the end of callee's scope. If the benaphore was already locked, this
procedure also blocks until the lock can be acquired.
When a lock has been acquired, all threads attempting to acquire a lock will be
blocked from entering any critical sections associated with the same benaphore,
until the lock is released.
This procedure always returns `true`. This makes it easy to define a critical
section by putting the function inside the `if` statement.
**Example**:
if benaphore_guard(&m) {
...
}
*/
@(deferred_in=benaphore_unlock) @(deferred_in=benaphore_unlock)
benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool { benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool {
benaphore_lock(m) benaphore_lock(m)
return true return true
} }
/*
Recursive benaphore.
Recurisve benaphore is just like a plain benaphore, except it allows reentrancy
into the critical section.
When a lock is acquired on a benaphore, all other threads attempting to
acquire a lock on the same benaphore will be blocked from any critical sections,
associated with the same benaphore.
When a lock is acquired on a benaphore by a thread, that thread is allowed
to acquire another lock on the same benaphore. When a thread has acquired the
lock on a benaphore, the benaphore will stay locked until the thread releases
the lock as many times as it has been locked by the thread.
*/
Recursive_Benaphore :: struct #no_copy { Recursive_Benaphore :: struct #no_copy {
counter: int, counter: int,
owner: int, owner: int,
@@ -229,6 +437,16 @@ Recursive_Benaphore :: struct #no_copy {
sema: Sema, sema: Sema,
} }
/*
Acquire a lock on a recursive benaphore.
This procedure acquires a lock on a recursive benaphore. If the benaphore is
held by another thread, this function blocks until the lock can be acquired.
Once a lock is acquired, all other threads attempting to acquire a lock will
be blocked from entering any critical sections associated with the same
recursive benaphore, until the lock is released.
*/
recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) { recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {
tid := current_thread_id() tid := current_thread_id()
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
@@ -241,6 +459,17 @@ recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {
b.recursion += 1 b.recursion += 1
} }
/*
Try to acquire a lock on a recursive benaphore.
This procedure attempts to acquire a lock on recursive benaphore. If the
benaphore is already held by a different thread, this procedure returns `false`.
Otherwise the lock is acquired and the procedure returns `true`.
If the lock is acquired, all other threads attempting to acquire a lock will
be blocked from entering any critical sections assciated with the same recursive
benaphore, until the lock is released.
*/
recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool { recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id() tid := current_thread_id()
if b.owner == tid { if b.owner == tid {
@@ -256,6 +485,13 @@ recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) ->
return true return true
} }
/*
Release a lock on a recursive benaphore.
This procedure releases a lock on the specified recursive benaphore. It also
causes the critical sections associated with the same benaphore, to become open
for other threads for entering.
*/
recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) { recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {
tid := current_thread_id() tid := current_thread_id()
_assert(tid == b.owner, "tid != b.owner") _assert(tid == b.owner, "tid != b.owner")
@@ -272,24 +508,50 @@ recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {
// outside the lock // outside the lock
} }
/*
Guard the current scope with a recursive benaphore.
This procedure acquires a lock on the specified recursive benaphores and
automatically releases it at the end of the callee's scope. If the recursive
benaphore was already held by a another thread, this procedure also blocks until
the lock can be acquired.
When the lock is acquired all other threads attempting to take a lock will be
blocked from entering any critical sections associated with the same benaphore,
until the lock is released.
This procedure always returns `true`, which makes it easy to define a critical
section by calling this procedure inside an `if` statement.
**Example**:
if recursive_benaphore_guard(&m) {
...
}
*/
@(deferred_in=recursive_benaphore_unlock) @(deferred_in=recursive_benaphore_unlock)
recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool { recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool {
recursive_benaphore_lock(m) recursive_benaphore_lock(m)
return true return true
} }
/*
Once action.
`Once` a synchronization primitive, that only allows a single entry into a
critical section from a single thread.
// Once is a data value that will perform exactly on action. */
//
// A Once must not be copied after first use.
Once :: struct #no_copy { Once :: struct #no_copy {
m: Mutex, m: Mutex,
done: bool, done: bool,
} }
// once_do calls the procedure fn if and only if once_do is being called for the first for this instance of Once. /*
Call a function once.
The `once_do` procedure group calls a specified function, if it wasn't already
called from the perspective of a specific `Once` struct.
*/
once_do :: proc{ once_do :: proc{
once_do_without_data, once_do_without_data,
once_do_without_data_contextless, once_do_without_data_contextless,
@@ -297,7 +559,9 @@ once_do :: proc{
once_do_with_data_contextless, once_do_with_data_contextless,
} }
// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once. /*
Call a function with no data once.
*/
once_do_without_data :: proc(o: ^Once, fn: proc()) { once_do_without_data :: proc(o: ^Once, fn: proc()) {
@(cold) @(cold)
do_slow :: proc(o: ^Once, fn: proc()) { do_slow :: proc(o: ^Once, fn: proc()) {
@@ -313,7 +577,9 @@ once_do_without_data :: proc(o: ^Once, fn: proc()) {
} }
} }
// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once. /*
Call a contextless function with no data once.
*/
once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) { once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) {
@(cold) @(cold)
do_slow :: proc(o: ^Once, fn: proc "contextless" ()) { do_slow :: proc(o: ^Once, fn: proc "contextless" ()) {
@@ -329,7 +595,9 @@ once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) {
} }
} }
// once_do_with_data calls the procedure fn if and only if once_do_with_data is being called for the first for this instance of Once. /*
Call a function with data once.
*/
once_do_with_data :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) { once_do_with_data :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
@(cold) @(cold)
do_slow :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) { do_slow :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
@@ -345,7 +613,9 @@ once_do_with_data :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
} }
} }
// once_do_with_data_contextless calls the procedure fn if and only if once_do_with_data_contextless is being called for the first for this instance of Once. /*
Call a contextless function with data once.
*/
once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) { once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
@(cold) @(cold)
do_slow :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) { do_slow :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
@@ -361,83 +631,112 @@ once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "context
} }
} }
/*
A Parker is an associated token which is initially not present:
* The `park` procedure blocks the current thread unless or until the token
is available, at which point the token is consumed.
* The `park_with_timeout` procedures works the same as `park` but only
// A Parker is an associated token which is initially not present: blocks for the specified duration.
// * The `park` procedure blocks the current thread unless or until the token * The `unpark` procedure automatically makes the token available if it
// is available, at which point the token is consumed. was not already.
// * The `park_with_timeout` procedures works the same as `park` but only */
// blocks for the specified duration.
// * The `unpark` procedure automatically makes the token available if it
// was not already.
Parker :: struct #no_copy { Parker :: struct #no_copy {
state: Futex, state: Futex,
} }
// Blocks the current thread until the token is made available. @(private="file") PARKER_EMPTY :: 0
// @(private="file") PARKER_NOTIFIED :: 1
// Assumes this is only called by the thread that owns the Parker. @(private="file") PARKER_PARKED :: max(u32)
/*
Blocks until the token is available.
This procedure blocks the execution of the current thread, until a token is
made available.
**Note**: This procedure assumes this is only called by the thread that owns
the Parker.
*/
park :: proc "contextless" (p: ^Parker) { park :: proc "contextless" (p: ^Parker) {
EMPTY :: 0 if atomic_sub_explicit(&p.state, 1, .Acquire) == PARKER_NOTIFIED {
NOTIFIED :: 1
PARKED :: max(u32)
if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
return return
} }
for { for {
futex_wait(&p.state, PARKED) futex_wait(&p.state, PARKER_PARKED)
if _, ok := atomic_compare_exchange_strong_explicit(&p.state, NOTIFIED, EMPTY, .Acquire, .Acquire); ok { if _, ok := atomic_compare_exchange_strong_explicit(&p.state, PARKER_NOTIFIED, PARKER_EMPTY, .Acquire, .Acquire); ok {
return return
} }
} }
} }
// Blocks the current thread until the token is made available, but only /*
// for a limited duration. Blocks until the token is available with timeout.
//
// Assumes this is only called by the thread that owns the Parker This procedure blocks the execution of the current thread until a token is made
available, or until the timeout has expired, whatever happens first.
**Note**: This procedure assumes this is only called by the thread that owns
the Parker.
*/
park_with_timeout :: proc "contextless" (p: ^Parker, duration: time.Duration) { park_with_timeout :: proc "contextless" (p: ^Parker, duration: time.Duration) {
EMPTY :: 0 start_tick := time.tick_now()
NOTIFIED :: 1 remaining_duration := duration
PARKED :: max(u32) if atomic_sub_explicit(&p.state, 1, .Acquire) == PARKER_NOTIFIED {
if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
return return
} }
futex_wait_with_timeout(&p.state, PARKED, duration) for {
atomic_exchange_explicit(&p.state, EMPTY, .Acquire) if !futex_wait_with_timeout(&p.state, PARKER_PARKED, remaining_duration) {
return
}
old, ok := atomic_compare_exchange_weak_explicit((^u32)(&p.state), PARKER_PARKED, PARKER_EMPTY, .Acquire, .Relaxed)
if ok || old == PARKER_PARKED {
return
}
end_tick := time.tick_now()
remaining_duration -= time.tick_diff(start_tick, end_tick)
start_tick = end_tick
}
} }
// Automatically makes thee token available if it was not already. /*
Make the token available.
*/
unpark :: proc "contextless" (p: ^Parker) { unpark :: proc "contextless" (p: ^Parker) {
EMPTY :: 0 if atomic_exchange_explicit((^u32)(&p.state), PARKER_NOTIFIED, .Release) == PARKER_PARKED {
NOTIFIED :: 1
PARKED :: max(Futex)
if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED {
futex_signal(&p.state) futex_signal(&p.state)
} }
} }
/*
One-shot event.
A one-shot event is an associated token which is initially not present:
// A One_Shot_Event is an associated token which is initially not present: * The `one_shot_event_wait` blocks the current thread until the event
// * The `one_shot_event_wait` blocks the current thread until the event is made available
// is made available * The `one_shot_event_signal` procedure automatically makes the token
// * The `one_shot_event_signal` procedure automatically makes the token available if its was not already.
// available if its was not already. */
One_Shot_Event :: struct #no_copy { One_Shot_Event :: struct #no_copy {
state: Futex, state: Futex,
} }
// Blocks the current thread until the event is made available with `one_shot_event_signal`. /*
Block until the event is made available.
This procedure blocks the execution of the current thread, until the event is
made available.
*/
one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) { one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
for atomic_load_explicit(&e.state, .Acquire) == 0 { for atomic_load_explicit(&e.state, .Acquire) == 0 {
futex_wait(&e.state, 0) futex_wait(&e.state, 0)
} }
} }
// Releases any threads that are currently blocked by this event with `one_shot_event_wait`. /*
Make event available.
*/
one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) { one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) {
atomic_store_explicit(&e.state, 1, .Release) atomic_store_explicit(&e.state, 1, .Release)
futex_broadcast(&e.state) futex_broadcast(&e.state)
+1 -2
View File
@@ -2,7 +2,6 @@
package sync package sync
import "core:c" import "core:c"
import "base:runtime"
import "core:sys/haiku" import "core:sys/haiku"
import "core:sys/unix" import "core:sys/unix"
import "core:time" import "core:time"
@@ -86,7 +85,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
waiter.prev.next = waiter.next waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev waiter.next.prev = waiter.prev
unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil) _ = unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
// FIXME: Add error handling! // FIXME: Add error handling!
return return
+417 -53
View File
@@ -3,46 +3,108 @@ package sync
import "base:runtime" import "base:runtime"
import "core:time" import "core:time"
/*
Obtain the current thread ID.
*/
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return _current_thread_id() return _current_thread_id()
} }
// A Mutex is a [[mutual exclusion lock; https://en.wikipedia.org/wiki/Mutual_exclusion]] /*
// It can be used to prevent more than one thread from executing the same piece of code, Mutual exclusion lock.
// and thus prevent access to same piece of memory by multiple threads, at the same time.
// A Mutex is a [[mutual exclusion lock; https://en.wikipedia.org/wiki/Mutual_exclusion]]
// A Mutex's zero value represents an initial, *unlocked* state. It can be used to prevent more than one thread from entering the critical
// section, and thus prevent access to same piece of memory by multiple threads, at
// If another thread tries to take the lock while another thread holds it, it will pause the same time.
// until the lock is released. Code or memory that is "surrounded" by a mutex lock is said
// to be "guarded by a mutex". Mutex's zero-initializzed value represents an initial, *unlocked* state.
//
// A Mutex must not be copied after first use (e.g., after locking it the first time). If another thread tries to acquire the lock, while it's already held (typically
// This is because, in order to coordinate with other threads, all threads must watch by another thread), the thread's execution will be blocked, until the lock is
// the same memory address to know when the lock has been released. Trying to use a released. Code or memory that is "surrounded" by a mutex lock and unlock
// copy of the lock at a different memory address will result in broken and unsafe operations is said to be "guarded by a mutex".
// behavior. For this reason, Mutexes are marked as `#no_copy`.
**Note**: A Mutex must not be copied after first use (e.g., after locking it the
first time). This is because, in order to coordinate with other threads, all
threads must watch the same memory address to know when the lock has been
released. Trying to use a copy of the lock at a different memory address will
result in broken and unsafe behavior. For this reason, Mutexes are marked as
`#no_copy`.
**Note**: If the current thread attempts to lock a mutex, while it's already
holding another lock, that will cause a trivial case of deadlock. Do not use
`Mutex` in recursive functions. In case multiple locks by the same thread are
desired, use `Recursive_Mutex`.
*/
Mutex :: struct #no_copy { Mutex :: struct #no_copy {
impl: _Mutex, impl: _Mutex,
} }
// mutex_lock locks m /*
Acquire a lock on a mutex.
This procedure acquires a lock with the specified mutex. If the mutex has been
already locked by any thread, this procedure also blocks until the lock can be
acquired.
Once the lock is acquired, all other threads that attempt to acquire a lock will
be blocked from entering any critical sections associated with the same mutex,
until the the lock is released.
**Note**: If the mutex is already locked by the current thread, a call to this
procedure will block indefinately. Do not use this in recursive procedures.
*/
mutex_lock :: proc "contextless" (m: ^Mutex) { mutex_lock :: proc "contextless" (m: ^Mutex) {
_mutex_lock(m) _mutex_lock(m)
} }
// mutex_unlock unlocks m /*
Release a lock on a mutex.
This procedure releases the lock associated with the specified mutex. If the
mutex was not locked, this operation is a no-op.
When the current thread, that holds a lock to the mutex calls `mutex_unlock`,
this allows one other thread waiting on the mutex to enter any critical sections
associated with the mutex. If there are no threads waiting on the mutex, the
critical sections will remain open.
*/
mutex_unlock :: proc "contextless" (m: ^Mutex) { mutex_unlock :: proc "contextless" (m: ^Mutex) {
_mutex_unlock(m) _mutex_unlock(m)
} }
// mutex_try_lock tries to lock m, will return true on success, and false on failure /*
Try to acquire a lock on a mutex.
This procedure tries to acquire a lock on the specified mutex. If it was already
locked, then the returned value is `false`, otherwise the lock is acquired and
the procedure returns `true`.
If the lock is acquired, all threads that attempt to acquire a lock will be
blocked from entering any critical sections associated with the same mutex,
until the lock is released.
*/
mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool { mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
return _mutex_try_lock(m) return _mutex_try_lock(m)
} }
/* /*
Example: Guard the current scope with a lock on a mutex.
This procedure acquires a mutex lock. The lock is automatically released
at the end of callee's scope. If the mutex was already locked, this procedure
also blocks until the lock can be acquired.
When a lock has been acquired, all threads attempting to acquire a lock will be
blocked from entering any critical sections associated with the mutex, until
the lock is released.
This procedure always returns `true`. This makes it easy to define a critical
section by putting the function inside the `if` statement.
**Example**:
if mutex_guard(&m) { if mutex_guard(&m) {
... ...
} }
@@ -53,47 +115,145 @@ mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
return true return true
} }
// A RW_Mutex is a reader/writer mutual exclusion lock /*
// The lock can be held by any arbitrary number of readers or a single writer Read-write mutual exclusion lock.
// The zero value for a RW_Mutex is an unlocked mutex
// An `RW_Mutex` is a reader/writer mutual exclusion lock. The lock can be held by
// A RW_Mutex must not be copied after first use any number of readers or a single writer.
This type of synchronization primitive supports two kinds of lock operations:
- Exclusive lock (write lock)
- Shared lock (read lock)
When an exclusive lock is acquired by any thread, all other threads, attempting
to acquire either an exclusive or shared lock, will be blocked from entering the
critical sections associated with the read-write mutex, until the exclusive
owner of the lock releases the lock.
When a shared lock is acquired by any thread, any other thread attempting to
acquire a shared lock will also be able to enter all the critical sections
associated with the read-write mutex. However threads attempting to acquire
an exclusive lock will be blocked from entering those critical sections, until
all shared locks are released.
**Note**: A read-write mutex must not be copied after first use (e.g., after
acquiring a lock). This is because, in order to coordinate with other threads,
all threads must watch the same memory address to know when the lock has been
released. Trying to use a copy of the lock at a different memory address will
result in broken and unsafe behavior. For this reason, mutexes are marked as
`#no_copy`.
**Note**: A read-write mutex is not recursive. Do not attempt to acquire an
exclusive lock more than once from the same thread, or an exclusive and shared
lock on the same thread. Taking a shared lock multiple times is acceptable.
*/
RW_Mutex :: struct #no_copy { RW_Mutex :: struct #no_copy {
impl: _RW_Mutex, impl: _RW_Mutex,
} }
// rw_mutex_lock locks rw for writing (with a single writer) /*
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available. Acquire an exclusive lock.
This procedure acquires an exclusive lock on the specified read-write mutex. If
the lock is already held by any thread, this procedure also blocks until the
lock can be acquired.
After a lock has been acquired, any thread attempting to acquire any lock
will be blocked from entering any critical sections associated with the same
read-write mutex, until the exclusive lock is released.
*/
rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) { rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_lock(rw) _rw_mutex_lock(rw)
} }
// rw_mutex_unlock unlocks rw for writing (with a single writer) /*
Release an exclusive lock.
This procedure releases an exclusive lock associated with the specified
read-write mutex.
When the exclusive lock is released, all critical sections, associated with the
same read-write mutex, become open to other threads.
*/
rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) { rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_unlock(rw) _rw_mutex_unlock(rw)
} }
// rw_mutex_try_lock tries to lock rw for writing (with a single writer) /*
Try to acquire an exclusive lock on a read-write mutex.
This procedure tries to acquire an exclusive lock on the specified read-write
mutex. If the mutex was already locked, the procedure returns `false`. Otherwise
it acquires the exclusive lock and returns `true`.
If the lock has been acquired, all threads attempting to acquire any lock
will be blocked from entering any critical sections associated with the same
read-write mutex, until the exclusive locked is released.
*/
rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool { rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return _rw_mutex_try_lock(rw) return _rw_mutex_try_lock(rw)
} }
// rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers) /*
Acquire a shared lock on a read-write mutex.
This procedure acquires a shared lock on the specified read-write mutex. If the
mutex already has an exclusive lock held, this procedure also blocks until the
lock can be acquired.
After the shared lock is obtained, all threads attempting to acquire an
exclusive lock will be blocked from entering any critical sections associated
with the same read-write mutex, until all shared locks associated with the
specified read-write mutex are released.
*/
rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) { rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_shared_lock(rw) _rw_mutex_shared_lock(rw)
} }
// rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers) /*
Release the shared lock on a read-write mutex.
This procedure releases shared lock on the specified read-write mutex. When all
shared locks are released, all critical sections associated with the same
read-write mutex become open to other threads.
*/
rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) { rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_shared_unlock(rw) _rw_mutex_shared_unlock(rw)
} }
// rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers) /*
Try to acquire a shared lock on a read-write mutex.
This procedure attempts to acquire a lock on the specified read-write mutex. If
the mutex already has an exclusive lock held, this procedure returns `false`.
Otherwise, it acquires the lock on the mutex and returns `true`.
If the shared lock has been acquired, it causes all threads attempting to
acquire the exclusive lock to be blocked from entering any critical sections
associated with the same read-write mutex, until all shared locks are released.
*/
rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool { rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return _rw_mutex_try_shared_lock(rw) return _rw_mutex_try_shared_lock(rw)
} }
/* /*
Example: Guard the current scope with an exclusive lock on a read-write mutex.
This procedure acquires an exclusive lock on the specified read-write mutex.
This procedure automatically releases the lock at the end of the callee's scope.
If the mutex was already locked by readers or a writer, this procedure blocks,
until a lock can be acquired.
When an exclusive lock is acquired, all other threads attempting to acquire an
exclusive lock will be blocked from entering any critical sections associated
with the same read-write mutex, until the exclusive lock is released.
This procedure always returns `true`, which makes it easy to define a critical
section by running this procedure inside an `if` statement.
**Example**:
if rw_mutex_guard(&m) { if rw_mutex_guard(&m) {
... ...
} }
@@ -105,8 +265,23 @@ rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
} }
/* /*
Example: Guard the current scope with a shared lock on a read-write mutex.
if rw_mutex_shared_guard(&m) {
This procedure acquires a shared lock on the specified read-write mutex. This
procedure automatically releases the lock at the end of the callee's scope. If
the mutex already has an associated exclusive lock, this procedure blocks, until
a lock can be acquired.
When a shared lock is obtained, all other threads attempting to obtain an
exclusive lock will be blocked from any critical sections, associated with the
same read-write mutex, until all shared locks are released.
This procedure always returns `true`, which makes it easy to define a critical
section by running this procedure inside an `if` statement.
**Example**:
if rw_mutex_guard(&m) {
... ...
} }
*/ */
@@ -116,30 +291,91 @@ rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
return true return true
} }
/*
Recursive mutual exclusion lock.
Recurisve mutex is just like a plain mutex, except it allows reentrancy. In
order for a thread to release the mutex for other threads, the mutex needs to
be unlocked as many times, as it was locked.
// A Recursive_Mutex is a recursive mutual exclusion lock When a lock is acquired on a recursive mutex, all other threads attempting to
// The zero value for a Recursive_Mutex is an unlocked mutex acquire a lock on the same mutex will be blocked from any critical sections,
// associated with the same recrusive mutex.
// A Recursive_Mutex must not be copied after first use
When a lock is acquired on a recursive mutex by a thread, that thread is allowed
to acquire another lock on the same mutex. When a thread has acquired the lock
on a recursive mutex, the recursive mutex will stay locked until the thread
releases the lock as many times as it has been locked by the thread.
**Note**: A recursive mutex must not be copied after first use (e.g., after
acquiring a lock). This is because, in order to coordinate with other threads,
all threads must watch the same memory address to know when the lock has been
released. Trying to use a copy of the lock at a different memory address will
result in broken and unsafe behavior. For this reason, mutexes are marked as
`#no_copy`.
*/
Recursive_Mutex :: struct #no_copy { Recursive_Mutex :: struct #no_copy {
impl: _Recursive_Mutex, impl: _Recursive_Mutex,
} }
/*
Acquire a lock on a recursive mutex.
This procedure acquires a lock on the specified recursive mutex. If the lock is
acquired by a different thread, this procedure also blocks until the lock can be
acquired.
When the lock is acquired, all other threads attempting to acquire a lock will
be blocked from entering any critical sections associated with the same mutex,
until the lock is released.
*/
recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) { recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
_recursive_mutex_lock(m) _recursive_mutex_lock(m)
} }
/*
Release a lock on a recursive mutex.
This procedure releases a lock on the specified recursive mutex. It also causes
the critical sections associated with the same mutex, to become open for other
threads for entering.
*/
recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) { recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
_recursive_mutex_unlock(m) _recursive_mutex_unlock(m)
} }
/*
Try to acquire a lock on a recursive mutex.
This procedure attempts to acquire a lock on the specified recursive mutex. If
the recursive mutex is locked by other threads, this procedure returns `false`.
Otherwise it locks the mutex and returns `true`.
If the lock is acquired, all other threads attempting to obtain a lock will be
blocked from entering any critical sections associated with the same mutex,
until the lock is released.
*/
recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool { recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
return _recursive_mutex_try_lock(m) return _recursive_mutex_try_lock(m)
} }
/* /*
Example: Guard the scope with a recursive mutex lock.
This procedure acquires a lock on the specified recursive mutex and
automatically releases it at the end of the callee's scope. If the recursive
mutex was already held by a another thread, this procedure also blocks until the
lock can be acquired.
When the lock is acquired all other threads attempting to take a lock will be
blocked from entering any critical sections associated with the same mutex,
until the lock is released.
This procedure always returns `true`, which makes it easy to define a critical
section by calling this procedure inside an `if` statement.
**Example**:
if recursive_mutex_guard(&m) { if recursive_mutex_guard(&m) {
... ...
} }
@@ -150,19 +386,69 @@ recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
return true return true
} }
/*
A condition variable.
// Cond implements a condition variable, a rendezvous point for threads `Cond` implements a condition variable, a rendezvous point for threads waiting
// waiting for signalling the occurence of an event for signalling the occurence of an event. Condition variables are used on
// conjuction with mutexes to provide a shared access to one or more shared
// A Cond must not be copied after first use variable.
A typical usage of condition variable is as follows. A thread that intends to
modify a shared variable shall:
1. Acquire a lock on a mutex.
2. Modify the shared memory.
3. Release the lock.
3. Call `cond_signal` or `cond_broadcast`.
A thread that intends to wait on a shared variable shall:
1. Acquire a lock on a mutex.
2. Call `cond_wait` or `cond_wait_with_timeout` (will release the mutex).
3. Check the condition and keep waiting in a loop if not satisfied with result.
**Note**: A condition variable must not be copied after first use (e.g., after
waiting on it the first time). This is because, in order to coordinate with
other threads, all threads must watch the same memory address to know when the
lock has been released. Trying to use a copy of the lock at a different memory
address will result in broken and unsafe behavior. For this reason, condition
variables are marked as `#no_copy`.
*/
Cond :: struct #no_copy { Cond :: struct #no_copy {
impl: _Cond, impl: _Cond,
} }
/*
Wait until the condition variable is signalled and release the associated mutex.
This procedure blocks the current thread until the specified condition variable
is signalled, or until a spurious wakeup occurs. In addition, if the condition
has been signalled, this procedure releases the lock on the specified mutex.
The mutex must be held by the calling thread, before calling the procedure.
**Note**: This procedure can return on a spurious wake-up, even if the condition
variable was not signalled by a thread.
*/
cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) { cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
_cond_wait(c, m) _cond_wait(c, m)
} }
/*
Wait until the condition variable is signalled or timeout is reached and release
the associated mutex.
This procedure blocks the current thread until the specified condition variable
is signalled, a timeout is reached, or until a spurious wakeup occurs. In
addition, if the condition has been signalled, this procedure releases the
lock on the specified mutex.
If the timeout was reached, this procedure returns `false`. Otherwise it returns
`true`.
Before this procedure is called the mutex must be held by the calling thread.
*/
cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool { cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
if duration <= 0 { if duration <= 0 {
return false return false
@@ -170,51 +456,123 @@ cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: tim
return _cond_wait_with_timeout(c, m, duration) return _cond_wait_with_timeout(c, m, duration)
} }
/*
Wake up one thread that waits on a condition variable.
This procedure causes exactly one thread waiting on the condition variable to
wake up.
*/
cond_signal :: proc "contextless" (c: ^Cond) { cond_signal :: proc "contextless" (c: ^Cond) {
_cond_signal(c) _cond_signal(c)
} }
/*
Wake up all threads that wait on a condition variable.
This procedure causes all threads waiting on the condition variable to wake up.
*/
cond_broadcast :: proc "contextless" (c: ^Cond) { cond_broadcast :: proc "contextless" (c: ^Cond) {
_cond_broadcast(c) _cond_broadcast(c)
} }
/*
Semaphore.
// When waited upon, blocks until the internal count is greater than zero, then subtracts one. When waited upon, semaphore blocks until the internal count is greater than
// Posting to the semaphore increases the count by one, or the provided amount. zero, then decrements the internal counter by one. Posting to the semaphore
// increases the count by one, or the provided amount.
// A Sema must not be copied after first use
This type of synchronization primitives can be useful for implementing queues.
The internal counter of the semaphore can be thought of as the amount of items
in the queue. After a data has been pushed to the queue, the thread shall call
`sema_post()` procedure, increasing the counter. When a thread takes an item
from the queue to do the job, it shall call `sema_wait()`, waiting on the
semaphore counter to become non-zero and decreasing it, if necessary.
**Note**: A semaphore must not be copied after first use (e.g., after posting
to it). This is because, in order to coordinate with other threads, all threads
must watch the same memory address to know when the lock has been released.
Trying to use a copy of the lock at a different memory address will result in
broken and unsafe behavior. For this reason, semaphores are marked as `#no_copy`.
*/
Sema :: struct #no_copy { Sema :: struct #no_copy {
impl: _Sema, impl: _Sema,
} }
/*
Increment the internal counter on a semaphore by the specified amount.
This procedure increments the internal counter of the semaphore. If any of the
threads were waiting on the semaphore, up to `count` of threads will continue
the execution and enter the critical section.
*/
sema_post :: proc "contextless" (s: ^Sema, count := 1) { sema_post :: proc "contextless" (s: ^Sema, count := 1) {
_sema_post(s, count) _sema_post(s, count)
} }
/*
Wait on a semaphore until the internal counter is non-zero.
This procedure blocks the execution of the current thread, until the semaphore
counter is non-zero, and atomically decrements it by one, once the wait has
ended.
*/
sema_wait :: proc "contextless" (s: ^Sema) { sema_wait :: proc "contextless" (s: ^Sema) {
_sema_wait(s) _sema_wait(s)
} }
/*
Wait on a semaphore until the internal counter is non-zero or a timeout is reached.
This procedure blocks the execution of the current thread, until the semaphore
counter is non-zero, and if so atomically decrements it by one, once the wait
has ended. If the specified timeout is reached, the function returns `false`,
otherwise it returns `true`.
*/
sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool { sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
return _sema_wait_with_timeout(s, duration) return _sema_wait_with_timeout(s, duration)
} }
/*
Fast userspace mutual exclusion lock.
Futex is a fast userspace mutual exclusion lock, that uses a pointer to a 32-bit
value as an identifier of the queue of waiting threads. The value pointed to
by that pointer can be used to store extra data.
// Futex is a fast userspace mutual exclusion lock, using a 32-bit memory address as a hint **IMPORTANT**: A futex must not be copied after first use (e.g., after waiting
// on it the first time, or signalling it). This is because, in order to coordinate
// An Futex must not be copied after first use with other threads, all threads must watch the same memory address. Trying to
use a copy of the lock at a different memory address will result in broken and
unsafe behavior.
*/
Futex :: distinct u32 Futex :: distinct u32
/*
Sleep if the futex contains the expected value until it's signalled.
If the value of the futex is `expected`, this procedure blocks the execution of
the current thread, until the futex is woken up, or until a spurious wakeup
occurs.
*/
futex_wait :: proc "contextless" (f: ^Futex, expected: u32) { futex_wait :: proc "contextless" (f: ^Futex, expected: u32) {
if u32(atomic_load_explicit(f, .Acquire)) != expected { if u32(atomic_load_explicit(f, .Acquire)) != expected {
return return
} }
ok := _futex_wait(f, expected)
_assert(_futex_wait(f, expected), "futex_wait failure") _assert(ok, "futex_wait failure")
} }
// returns true if the wait happened within the duration, false if it exceeded the time duration /*
Sleep if the futex contains the expected value until it's signalled or the
timeout is reached.
If the value of the futex is `expected`, this procedure blocks the execution of
the current thread, until the futex is signalled, a timeout is reached, or
until a spurious wakeup occurs.
This procedure returns `false` if the timeout was reached, `true` otherwise.
*/
futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool { futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
if u32(atomic_load_explicit(f, .Acquire)) != expected { if u32(atomic_load_explicit(f, .Acquire)) != expected {
return true return true
@@ -226,10 +584,16 @@ futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duratio
return _futex_wait_with_timeout(f, expected, duration) return _futex_wait_with_timeout(f, expected, duration)
} }
/*
Wake up a single thread waiting on a futex.
*/
futex_signal :: proc "contextless" (f: ^Futex) { futex_signal :: proc "contextless" (f: ^Futex) {
_futex_signal(f) _futex_signal(f)
} }
/*
Wake up multiple threads waiting on a futex.
*/
futex_broadcast :: proc "contextless" (f: ^Futex) { futex_broadcast :: proc "contextless" (f: ^Futex) {
_futex_broadcast(f) _futex_broadcast(f)
} }
@@ -29,3 +29,7 @@ OpenPanel_setResolvesAliases :: proc "c" (self: ^OpenPanel, setting: BOOL) {
OpenPanel_setAllowsMultipleSelection :: proc "c" (self: ^OpenPanel, setting: BOOL) { OpenPanel_setAllowsMultipleSelection :: proc "c" (self: ^OpenPanel, setting: BOOL) {
msgSend(nil, self, "setAllowsMultipleSelection:", setting) msgSend(nil, self, "setAllowsMultipleSelection:", setting)
} }
@(objc_type=OpenPanel, objc_name="setAllowedFileTypes")
OpenPanel_setAllowedFileTypes :: proc "c" (self: ^OpenPanel, types: ^Array) {
msgSend(nil, self, "setAllowedFileTypes:", types)
}
+1 -1
View File
@@ -399,7 +399,7 @@ cpu_topology_node_info :: struct {
}, },
_package: struct { _package: struct {
vendor: cpu_vendor, vendor: cpu_vendor,
cache_line_size: u32 cache_line_size: u32,
}, },
_core: struct { _core: struct {
model: u32, model: u32,
+1
View File
@@ -16,6 +16,7 @@ CLOCK_REALTIME :: clockid_t(2)
CLOCK_THREAD_CPUTIME_ID :: clockid_t(3) CLOCK_THREAD_CPUTIME_ID :: clockid_t(3)
errno_t :: enum u16 { errno_t :: enum u16 {
NONE = 0,
// No error occurred. System call completed successfully. // No error occurred. System call completed successfully.
SUCCESS = 0, SUCCESS = 0,
// Argument list too long. // Argument list too long.
+37
View File
@@ -152,6 +152,43 @@ foreign advapi32 {
cbData: DWORD, cbData: DWORD,
) -> LSTATUS --- ) -> LSTATUS ---
RegQueryInfoKeyW :: proc(
hKey: HKEY,
lpClass: LPWSTR,
lpcchClass: LPDWORD,
lpReserved: LPDWORD,
lpcSubKeys: LPDWORD,
lpcbMaxSubKeyLen: LPDWORD,
lpcbMaxClassLen: LPDWORD,
lpcValues: LPDWORD,
lpcbMaxValueNameLen: LPDWORD,
lpcbMaxValueLen: LPDWORD,
lpcbSecurityDescriptor: LPDWORD,
lpftLastWriteTime: ^FILETIME,
) -> LSTATUS ---
RegEnumKeyExW :: proc(
hKey: HKEY,
dwIndex: DWORD,
lpName: LPWSTR,
lpcchName: LPDWORD,
lpReserved: LPDWORD,
lpClass: LPWSTR,
lpcchClass: LPDWORD,
lpftLastWriteTime: ^FILETIME,
) -> LSTATUS ---
RegEnumValueW :: proc(
hKey: HKEY,
dwIndex: DWORD,
lpValueName: LPWSTR,
lpcchValueName: LPDWORD,
lpReserved: LPDWORD,
lpType: LPDWORD,
lpData: LPBYTE,
lpcbData: LPDWORD,
) -> LSTATUS ---
GetFileSecurityW :: proc( GetFileSecurityW :: proc(
lpFileName: LPCWSTR, lpFileName: LPCWSTR,
RequestedInformation: SECURITY_INFORMATION, RequestedInformation: SECURITY_INFORMATION,
+298
View File
@@ -0,0 +1,298 @@
// +build windows
package sys_windows
// https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
CODEPAGE :: enum UINT {
// Default to ANSI code page
ACP = CP_ACP,
// Default to OEM code page
OEMCP = CP_OEMCP,
// Default to MAC code page
MACCP = CP_MACCP,
// Current thread's ANSI code page
THREAD_ACP = CP_THREAD_ACP,
// Symbol translations
SYMBOL = CP_SYMBOL,
// IBM EBCDIC US-Canada
IBM037 = 037,
// OEM United States
IBM437 = 437,
// IBM EBCDIC International
IBM500 = 500,
// Arabic (ASMO 708)
ASMO_708 = 708,
// Arabic (Transparent ASMO); Arabic (DOS)
DOS_720 = 720,
// OEM Greek (formerly 437G); Greek (DOS)
IBM737 = 737,
// OEM Baltic; Baltic (DOS)
IBM775 = 775,
// OEM Multilingual Latin 1; Western European (DOS)
IBM850 = 850,
// OEM Latin 2; Central European (DOS)
IBM852 = 852,
// OEM Cyrillic (primarily Russian)
IBM855 = 855,
// OEM Turkish; Turkish (DOS)
IBM857 = 857,
// OEM Multilingual Latin 1 + Euro symbol
IBM00858 = 858,
// OEM Portuguese; Portuguese (DOS)
IBM860 = 860,
// OEM Icelandic; Icelandic (DOS)
IBM861 = 861,
// OEM Hebrew; Hebrew (DOS)
DOS_862 = 862,
// OEM French Canadian; French Canadian (DOS)
IBM863 = 863,
// OEM Arabic; Arabic (864)
IBM864 = 864,
// OEM Nordic; Nordic (DOS)
IBM865 = 865,
// OEM Russian; Cyrillic (DOS)
CP866 = 866,
// OEM Modern Greek; Greek, Modern (DOS)
IBM869 = 869,
// IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
IBM870 = 870,
// Thai (Windows)
WINDOWS_874 = 874,
// IBM EBCDIC Greek Modern
CP875 = 875,
// ANSI/OEM Japanese; Japanese (Shift-JIS)
SHIFT_JIS = 932,
// ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
GB2312 = 936,
// ANSI/OEM Korean (Unified Hangul Code)
KS_C_5601_1987 = 949,
// ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
BIG5 = 950,
// IBM EBCDIC Turkish (Latin 5)
IBM1026 = 1026,
// IBM EBCDIC Latin 1/Open System
IBM01047 = 1047,
// IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
IBM01140 = 1140,
// IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
IBM01141 = 1141,
// IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
IBM01142 = 1142,
// IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
IBM01143 = 1143,
// IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
IBM01144 = 1144,
// IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
IBM01145 = 1145,
// IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
IBM01146 = 1146,
// IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
IBM01147 = 1147,
// IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
IBM01148 = 1148,
// IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
IBM01149 = 1149,
// Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
UTF16 = 1200,
// Unicode UTF-16, big endian byte order; available only to managed applications
UNICODEFFFE = 1201,
// ANSI Central European; Central European (Windows)
WINDOWS_1250 = 1250,
// ANSI Cyrillic; Cyrillic (Windows)
WINDOWS_1251 = 1251,
// ANSI Latin 1; Western European (Windows)
WINDOWS_1252 = 1252,
// ANSI Greek; Greek (Windows)
WINDOWS_1253 = 1253,
// ANSI Turkish; Turkish (Windows)
WINDOWS_1254 = 1254,
// ANSI Hebrew; Hebrew (Windows)
WINDOWS_1255 = 1255,
// ANSI Arabic; Arabic (Windows)
WINDOWS_1256 = 1256,
// ANSI Baltic; Baltic (Windows)
WINDOWS_1257 = 1257,
// ANSI/OEM Vietnamese; Vietnamese (Windows)
WINDOWS_1258 = 1258,
// Korean (Johab)
JOHAB = 1361,
// MAC Roman; Western European (Mac)
MACINTOSH = 10000,
// Japanese (Mac)
X_MAC_JAPANESE = 10001,
// MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
X_MAC_CHINESETRAD = 10002,
// Korean (Mac)
X_MAC_KOREAN = 10003,
// Arabic (Mac)
X_MAC_ARABIC = 10004,
// Hebrew (Mac)
X_MAC_HEBREW = 10005,
// Greek (Mac)
X_MAC_GREEK = 10006,
// Cyrillic (Mac)
X_MAC_CYRILLIC = 10007,
// MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
X_MAC_CHINESESIMP = 10008,
// Romanian (Mac)
X_MAC_ROMANIAN = 10010,
// Ukrainian (Mac)
X_MAC_UKRAINIAN = 10017,
// Thai (Mac)
X_MAC_THAI = 10021,
// MAC Latin 2; Central European (Mac)
X_MAC_CE = 10029,
// Icelandic (Mac)
X_MAC_ICELANDIC = 10079,
// Turkish (Mac)
X_MAC_TURKISH = 10081,
// Croatian (Mac)
X_MAC_CROATIAN = 10082,
// Unicode UTF-32, little endian byte order; available only to managed applications
UTF32 = 12000,
// Unicode UTF-32, big endian byte order; available only to managed applications
UTF32BE = 12001,
// CNS Taiwan; Chinese Traditional (CNS)
X_CHINESE_CNS = 20000,
// TCA Taiwan
X_CP20001 = 20001,
// Eten Taiwan; Chinese Traditional (Eten)
X_CHINESE_ETEN = 20002,
// IBM5550 Taiwan
X_CP20003 = 20003,
// TeleText Taiwan
X_CP20004 = 20004,
// Wang Taiwan
X_CP20005 = 20005,
// IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
X_IA5 = 20105,
// IA5 German (7-bit)
X_IA5_GERMAN = 20106,
// IA5 Swedish (7-bit)
X_IA5_SWEDISH = 20107,
// IA5 Norwegian (7-bit)
X_IA5_NORWEGIAN = 20108,
// US-ASCII (7-bit)
US_ASCII = 20127,
// T.61
X_CP20261 = 20261,
// ISO 6937 Non-Spacing Accent
X_CP20269 = 20269,
// IBM EBCDIC Germany
IBM273 = 20273,
// IBM EBCDIC Denmark-Norway
IBM277 = 20277,
// IBM EBCDIC Finland-Sweden
IBM278 = 20278,
// IBM EBCDIC Italy
IBM280 = 20280,
// IBM EBCDIC Latin America-Spain
IBM284 = 20284,
// IBM EBCDIC United Kingdom
IBM285 = 20285,
// IBM EBCDIC Japanese Katakana Extended
IBM290 = 20290,
// IBM EBCDIC France
IBM297 = 20297,
// IBM EBCDIC Arabic
IBM420 = 20420,
// IBM EBCDIC Greek
IBM423 = 20423,
// IBM EBCDIC Hebrew
IBM424 = 20424,
// IBM EBCDIC Korean Extended
X_EBCDIC_KOREANEXTENDED = 20833,
// IBM EBCDIC Thai
IBM_THAI = 20838,
// Russian (KOI8-R); Cyrillic (KOI8-R)
KOI8_R = 20866,
// IBM EBCDIC Icelandic
IBM871 = 20871,
// IBM EBCDIC Cyrillic Russian
IBM880 = 20880,
// IBM EBCDIC Turkish
IBM905 = 20905,
// IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
IBM00924 = 20924,
// Japanese (JIS 0208-1990 and 0212-1990)
EUC_JP = 20932,
// Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
X_CP20936 = 20936,
// Korean Wansung
X_CP20949 = 20949,
// IBM EBCDIC Cyrillic Serbian-Bulgarian
CP1025 = 21025,
// Ukrainian (KOI8-U); Cyrillic (KOI8-U)
KOI8_U = 21866,
// ISO 8859-1 Latin 1; Western European (ISO)
ISO_8859_1 = 28591,
// ISO 8859-2 Central European; Central European (ISO)
ISO_8859_2 = 28592,
// ISO 8859-3 Latin 3
ISO_8859_3 = 28593,
// ISO 8859-4 Baltic
ISO_8859_4 = 28594,
// ISO 8859-5 Cyrillic
ISO_8859_5 = 28595,
// ISO 8859-6 Arabic
ISO_8859_6 = 28596,
// ISO 8859-7 Greek
ISO_8859_7 = 28597,
// ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
ISO_8859_8 = 28598,
// ISO 8859-9 Turkish
ISO_8859_9 = 28599,
// ISO 8859-13 Estonian
ISO_8859_13 = 28603,
// ISO 8859-15 Latin 9
ISO_8859_15 = 28605,
// Europa 3
X_EUROPA = 29001,
// ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
ISO_8859_8_I = 38598,
// ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
ISO_2022_JP = 50220,
// ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
CSISO2022JP = 50221,
// ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
ISO_2022_2_JP = 50222,
// ISO 2022 Korean
ISO_2022_KR = 50225,
// ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
X_CP50227 = 50227,
// EUC Japanese
EUC_JP_2 = 51932,
// EUC Simplified Chinese; Chinese Simplified (EUC)
EUC_CN = 51936,
// EUC Korean
EUC_KR = 51949,
// HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
HZ_GB_2312 = 52936,
// **Windows XP and later:** GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
GB18030 = 54936,
// ISCII Devanagari
X_ISCII_DE = 57002,
// ISCII Bangla
X_ISCII_BE = 57003,
// ISCII Tamil
X_ISCII_TA = 57004,
// ISCII Telugu
X_ISCII_TE = 57005,
// ISCII Assamese
X_ISCII_AS = 57006,
// ISCII Odia
X_ISCII_OR = 57007,
// ISCII Kannada
X_ISCII_KA = 57008,
// ISCII Malayalam
X_ISCII_MA = 57009,
// ISCII Gujarati
X_ISCII_GU = 57010,
// ISCII Punjabi
X_ISCII_PA = 57011,
// Unicode (UTF-7)
UTF7 = CP_UTF7, /*65000*/
// Unicode (UTF-8)
UTF8 = CP_UTF8, /*65001*/
}
+288 -75
View File
@@ -7,108 +7,97 @@ foreign import gdi32 "system:Gdi32.lib"
@(default_calling_convention="system") @(default_calling_convention="system")
foreign gdi32 { foreign gdi32 {
GetStockObject :: proc(i: c_int) -> HGDIOBJ --- GetDeviceCaps :: proc(hdc: HDC, index: INT) -> INT ---
GetStockObject :: proc(i: INT) -> HGDIOBJ ---
SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ --- SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ ---
DeleteObject :: proc(ho: HGDIOBJ) -> BOOL --- DeleteObject :: proc(ho: HGDIOBJ) -> BOOL ---
SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF --- SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
SetBkMode :: proc(hdc: HDC, mode: BKMODE) -> INT ---
CreateCompatibleDC :: proc(hdc: HDC) -> HDC --- CreateCompatibleDC :: proc(hdc: HDC) -> HDC ---
DeleteDC :: proc(hdc: HDC) -> BOOL --- DeleteDC :: proc(hdc: HDC) -> BOOL ---
CancelDC :: proc(hdc: HDC) -> BOOL ---
SaveDC :: proc(hdc: HDC) -> INT ---
RestoreDC :: proc(hdc: HDC, nSavedDC: INT) -> BOOL ---
CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH --- CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
CreateDIBitmap :: proc(hdc: HDC, pbmih: ^BITMAPINFOHEADER, flInit: DWORD, pjBits: VOID, pbmi: ^BITMAPINFO, iUsage: UINT) -> HBITMAP ---
CreateDIBSection :: proc(hdc: HDC, pbmi: ^BITMAPINFO, usage: UINT, ppvBits: VOID, hSection: HANDLE, offset: DWORD) -> HBITMAP ---
StretchDIBits :: proc(hdc: HDC, xDest, yDest, DestWidth, DestHeight, xSrc, ySrc, SrcWidth, SrcHeight: INT, lpBits: VOID, lpbmi: ^BITMAPINFO, iUsage: UINT, rop: DWORD) -> INT ---
StretchBlt :: proc(hdcDest: HDC, xDest, yDest, wDest, hDest: INT, hdcSrc: HDC, xSrc, ySrc, wSrc, hSrc: INT, rop: DWORD) -> BOOL ---
CreateDIBitmap :: proc( SetPixelFormat :: proc(hdc: HDC, format: INT, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
hdc: HDC, ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> INT ---
pbmih: ^BITMAPINFOHEADER, DescribePixelFormat :: proc(hdc: HDC, iPixelFormat: INT, nBytes: UINT, ppfd: ^PIXELFORMATDESCRIPTOR) -> INT ---
flInit: DWORD, SwapBuffers :: proc(hdc: HDC) -> BOOL ---
pjBits: VOID,
pbmi: ^BITMAPINFO,
iUsage: UINT,
) -> HBITMAP ---
CreateDIBSection :: proc(
hdc: HDC,
pbmi: ^BITMAPINFO,
usage: UINT,
ppvBits: VOID,
hSection: HANDLE,
offset: DWORD,
) -> HBITMAP ---
StretchDIBits :: proc(
hdc: HDC,
xDest: c_int,
yDest: c_int,
DestWidth: c_int,
DestHeight: c_int,
xSrc: c_int,
ySrc: c_int,
SrcWidth: c_int,
SrcHeight: c_int,
lpBits: VOID,
lpbmi: ^BITMAPINFO,
iUsage: UINT,
rop: DWORD,
) -> c_int ---
StretchBlt :: proc(
hdcDest: HDC,
xDest: c_int,
yDest: c_int,
wDest: c_int,
hDest: c_int,
hdcSrc: HDC,
xSrc: c_int,
ySrc: c_int,
wSrc: c_int,
hSrc: c_int,
rop: DWORD,
) -> BOOL ---
SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
DescribePixelFormat :: proc(hdc: HDC, iPixelFormat: c_int, nBytes: UINT, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
SwapBuffers :: proc(HDC) -> BOOL ---
SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF --- SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
GetDCBrushColor :: proc(hdc: HDC) -> COLORREF --- GetDCBrushColor :: proc(hdc: HDC) -> COLORREF ---
PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL --- PatBlt :: proc(hdc: HDC, x, y, w, h: INT, rop: DWORD) -> BOOL ---
Rectangle :: proc(hdc: HDC, left, top, right, bottom: c_int) -> BOOL --- Rectangle :: proc(hdc: HDC, left, top, right, bottom: INT) -> BOOL ---
CreateFontW :: proc( CreateFontW :: proc(cHeight, cWidth, cEscapement, cOrientation, cWeight: INT, bItalic, bUnderline, bStrikeOut, iCharSet, iOutPrecision: DWORD, iClipPrecision, iQuality, iPitchAndFamily: DWORD, pszFaceName: LPCWSTR) -> HFONT ---
cHeight, cWidth, cEscapement, cOrientation, cWeight: c_int, CreateFontIndirectW :: proc(lplf: ^LOGFONTW) -> HFONT ---
bItalic, bUnderline, bStrikeOut, iCharSet, iOutPrecision: DWORD, CreateFontIndirectExW :: proc(unnamedParam1: ^ENUMLOGFONTEXDVW) -> HFONT ---
iClipPrecision, iQuality, iPitchAndFamily: DWORD, AddFontResourceW :: proc(unnamedParam1: LPCWSTR) -> INT ---
pszFaceName: LPCWSTR, AddFontResourceExW :: proc(name: LPCWSTR, fl: DWORD, res: PVOID) -> INT ---
) -> HFONT --- AddFontMemResourceEx :: proc(pFileView: PVOID, cjSize: DWORD, pvResrved: PVOID, pNumFonts: ^DWORD) -> HANDLE ---
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL --- EnumFontsW :: proc(hdc: HDC, lpLogfont: LPCWSTR, lpProc: FONTENUMPROCW, lParam: LPARAM) -> INT ---
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL --- EnumFontFamiliesW :: proc(hdc: HDC, lpLogfont: LPCWSTR, lpProc: FONTENUMPROCW, lParam: LPARAM) -> INT ---
EnumFontFamiliesExW :: proc(hdc: HDC, lpLogfont: LPLOGFONTW, lpProc: FONTENUMPROCW, lParam: LPARAM, dwFlags: DWORD) -> INT ---
TextOutW :: proc(hdc: HDC, x, y: INT, lpString: LPCWSTR, c: INT) -> BOOL ---
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: INT, psizl: LPSIZE) -> BOOL ---
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL --- GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH --- CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH ---
GetObjectW :: proc(h: HANDLE, c: c_int, pv: LPVOID) -> int --- GetObjectW :: proc(h: HANDLE, c: INT, pv: LPVOID) -> int ---
CreateCompatibleBitmap :: proc(hdc: HDC, cx, cy: c_int) -> HBITMAP --- CreateCompatibleBitmap :: proc(hdc: HDC, cx, cy: INT) -> HBITMAP ---
BitBlt :: proc(hdc: HDC, x, y, cx, cy: c_int, hdcSrc: HDC, x1, y1: c_int, rop: DWORD) -> BOOL --- BitBlt :: proc(hdc: HDC, x, y, cx, cy: INT, hdcSrc: HDC, x1, y1: INT, rop: DWORD) -> BOOL ---
GetDIBits :: proc(hdc: HDC, hbm: HBITMAP, start, cLines: UINT, lpvBits: LPVOID, lpbmi: ^BITMAPINFO, usage: UINT) -> int --- GetDIBits :: proc(hdc: HDC, hbm: HBITMAP, start, cLines: UINT, lpvBits: LPVOID, lpbmi: ^BITMAPINFO, usage: UINT) -> INT ---
SetDIBits :: proc(hdc: HDC, hbm: HBITMAP, start: UINT, cLines: UINT, lpBits: VOID, lpbmi: ^BITMAPINFO, ColorUse: UINT) -> INT ---
SetDIBColorTable :: proc(hdc: HDC, iStart: UINT, cEntries: UINT, prgbq: ^RGBQUAD) -> UINT ---
GetDIBColorTable :: proc(hdc: HDC, iStart: UINT, cEntries: UINT, prgbq: ^RGBQUAD) -> UINT ---
CreatePen :: proc(iStyle, cWidth: INT, color: COLORREF) -> HPEN ---
ExtCreatePen :: proc(iPenStyle, cWidth: DWORD, plbrush: ^LOGBRUSH, cStyle: DWORD, pstyle: ^DWORD) -> HPEN ---
SetDCPenColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
GetDCPenColor :: proc(hdc: HDC) -> COLORREF ---
CreatePalette :: proc(plpal: ^LOGPALETTE) -> HPALETTE ---
SelectPalette :: proc(hdc: HDC, hPal: HPALETTE, bForceBkgd: BOOL) -> HPALETTE ---
RealizePalette :: proc(hdc: HDC) -> UINT ---
SetTextColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
RoundRect :: proc(hdc: HDC, left: INT, top: INT, right: INT, bottom: INT, width: INT, height: INT) -> BOOL ---
SetPixel :: proc(hdc: HDC, x: INT, y: INT, color: COLORREF) -> COLORREF ---
GdiTransparentBlt :: proc(hdcDest: HDC, xoriginDest, yoriginDest, wDest, hDest: INT, hdcSrc: HDC, xoriginSrc, yoriginSrc, wSrc, hSrc: INT, crTransparent: UINT) -> BOOL ---
GdiGradientFill :: proc(hdc: HDC, pVertex: PTRIVERTEX, nVertex: ULONG, pMesh: PVOID, nCount: ULONG, ulMode: ULONG) -> BOOL ---
GdiAlphaBlend :: proc(hdcDest: HDC, xoriginDest, yoriginDest, wDest, hDest: INT, hdcSrc: HDC, xoriginSrc, yoriginSrc, wSrc, hSrc: INT, ftn: BLENDFUNCTION) -> BOOL ---
} }
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF { RGB :: #force_inline proc "contextless" (#any_int r, g, b: int) -> COLORREF {
return transmute(COLORREF)[4]u8{r, g, b, 0} return COLORREF(DWORD(BYTE(r)) | (DWORD(BYTE(g)) << 8) | (DWORD(BYTE(b)) << 16))
}
PALETTERGB :: #force_inline proc "contextless" (#any_int r, g, b: int) -> COLORREF {
return 0x02000000 | RGB(r, g, b)
}
PALETTEINDEX :: #force_inline proc "contextless" (#any_int i: int) -> COLORREF {
return COLORREF(DWORD(0x01000000) | DWORD(WORD(i)))
} }
FXPT2DOT30 :: distinct fixed.Fixed(i32, 30) FXPT2DOT30 :: distinct fixed.Fixed(i32, 30)
CIEXYZ :: struct { CIEXYZ :: struct {
ciexyzX: FXPT2DOT30, ciexyzX, ciexyzY, ciexyzZ: FXPT2DOT30,
ciexyzY: FXPT2DOT30,
ciexyzZ: FXPT2DOT30,
} }
CIEXYZTRIPLE :: struct { CIEXYZTRIPLE :: struct {
ciexyzRed: CIEXYZ, ciexyzRed, ciexyzGreen, ciexyzBlue: CIEXYZ,
ciexyzGreen: CIEXYZ,
ciexyzBlue: CIEXYZ,
} }
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
@@ -138,3 +127,227 @@ BITMAPV5HEADER :: struct {
bV5ProfileSize: DWORD, bV5ProfileSize: DWORD,
bV5Reserved: DWORD, bV5Reserved: DWORD,
} }
PALETTEENTRY :: struct {
peRed, peGreen, peBlue, peFlags: BYTE,
}
LOGPALETTE :: struct {
palVersion: WORD,
palNumEntries: WORD,
palPalEntry: []PALETTEENTRY,
}
BKMODE :: enum {
TRANSPARENT = 1,
OPAQUE = 2,
}
ICONINFOEXW :: struct {
cbSize: DWORD,
fIcon: BOOL,
xHotspot, yHotspot: DWORD,
hbmMask, hbmColor: HBITMAP,
wResID: WORD,
szModName: [MAX_PATH]WCHAR,
szResName: [MAX_PATH]WCHAR,
}
PICONINFOEXW :: ^ICONINFOEXW
AC_SRC_OVER :: 0x00
AC_SRC_ALPHA :: 0x01
TransparentBlt :: GdiTransparentBlt
GradientFill :: GdiGradientFill
AlphaBlend :: GdiAlphaBlend
COLOR16 :: USHORT
TRIVERTEX :: struct {
x, y: LONG,
Red, Green, Blue, Alpha: COLOR16,
}
PTRIVERTEX :: ^TRIVERTEX
GRADIENT_TRIANGLE :: struct {
Vertex1, Vertex2, Vertex3: ULONG,
}
PGRADIENT_TRIANGLE :: ^GRADIENT_TRIANGLE
GRADIENT_RECT :: struct {
UpperLeft, LowerRight: ULONG,
}
PGRADIENT_RECT :: ^GRADIENT_RECT
BLENDFUNCTION :: struct {
BlendOp, BlendFlags, SourceConstantAlpha, AlphaFormat: BYTE,
}
GRADIENT_FILL_RECT_H : ULONG : 0x00000000
GRADIENT_FILL_RECT_V : ULONG : 0x00000001
GRADIENT_FILL_TRIANGLE : ULONG : 0x00000002
GRADIENT_FILL_OP_FLAG : ULONG : 0x000000ff
/* Brush Styles */
BS_SOLID :: 0
BS_NULL :: 1
BS_HOLLOW :: BS_NULL
BS_HATCHED :: 2
BS_PATTERN :: 3
BS_INDEXED :: 4
BS_DIBPATTERN :: 5
BS_DIBPATTERNPT :: 6
BS_PATTERN8X8 :: 7
BS_DIBPATTERN8X8 :: 8
BS_MONOPATTERN :: 9
/* Hatch Styles */
HS_HORIZONTAL :: 0 /* ----- */
HS_VERTICAL :: 1 /* ||||| */
HS_FDIAGONAL :: 2 /* \\\\\ */
HS_BDIAGONAL :: 3 /* ///// */
HS_CROSS :: 4 /* +++++ */
HS_DIAGCROSS :: 5 /* xxxxx */
HS_API_MAX :: 12
/* Pen Styles */
PS_SOLID :: 0
PS_DASH :: 1 /* ------- */
PS_DOT :: 2 /* ....... */
PS_DASHDOT :: 3 /* _._._._ */
PS_DASHDOTDOT :: 4 /* _.._.._ */
PS_NULL :: 5
PS_INSIDEFRAME :: 6
PS_USERSTYLE :: 7
PS_ALTERNATE :: 8
PS_STYLE_MASK :: 0x0000000F
PS_ENDCAP_ROUND :: 0x00000000
PS_ENDCAP_SQUARE :: 0x00000100
PS_ENDCAP_FLAT :: 0x00000200
PS_ENDCAP_MASK :: 0x00000F00
PS_JOIN_ROUND :: 0x00000000
PS_JOIN_BEVEL :: 0x00001000
PS_JOIN_MITER :: 0x00002000
PS_JOIN_MASK :: 0x0000F000
PS_COSMETIC :: 0x00000000
PS_GEOMETRIC :: 0x00010000
PS_TYPE_MASK :: 0x000F0000
LOGBRUSH :: struct {
lbStyle: UINT,
lbColor: COLORREF,
lbHatch: ULONG_PTR,
}
PLOGBRUSH :: ^LOGBRUSH
/* CombineRgn() Styles */
RGN_AND :: 1
RGN_OR :: 2
RGN_XOR :: 3
RGN_DIFF :: 4
RGN_COPY :: 5
/* StretchBlt() Modes */
// BLACKONWHITE :: 1
// WHITEONBLACK :: 2
// COLORONCOLOR :: 3
// HALFTONE :: 4
/* PolyFill() Modes */
ALTERNATE :: 1
WINDING :: 2
/* Layout Orientation Options */
LAYOUT_RTL :: 0x00000001 // Right to left
LAYOUT_BTT :: 0x00000002 // Bottom to top
LAYOUT_VBH :: 0x00000004 // Vertical before horizontal
LAYOUT_ORIENTATIONMASK :: (LAYOUT_RTL | LAYOUT_BTT | LAYOUT_VBH)
/* Text Alignment Options */
TA_NOUPDATECP :: 0
TA_UPDATECP :: 1
TA_LEFT :: 0
TA_RIGHT :: 2
TA_CENTER :: 6
TA_TOP :: 0
TA_BOTTOM :: 8
TA_BASELINE :: 24
TA_RTLREADING :: 256
TA_MASK :: (TA_BASELINE+TA_CENTER+TA_UPDATECP+TA_RTLREADING)
MM_MAX_NUMAXES :: 16
DESIGNVECTOR :: struct {
dvReserved: DWORD,
dvNumAxes: DWORD,
dvValues: [MM_MAX_NUMAXES]LONG,
}
LF_FACESIZE :: 32
LF_FULLFACESIZE :: 64
LOGFONTW :: struct {
lfHeight: LONG,
lfWidth: LONG,
lfEscapement: LONG,
lfOrientation: LONG,
lfWeight: LONG,
lfItalic: BYTE,
lfUnderline: BYTE,
lfStrikeOut: BYTE,
lfCharSet: BYTE,
lfOutPrecision: BYTE,
lfClipPrecision: BYTE,
lfQuality: BYTE,
lfPitchAndFamily: BYTE,
lfFaceName: [LF_FACESIZE]WCHAR,
}
LPLOGFONTW :: ^LOGFONTW
ENUMLOGFONTW :: struct {
elfLogFont: LOGFONTW,
elfFullName: [LF_FULLFACESIZE]WCHAR,
elfStyle: [LF_FACESIZE]WCHAR,
}
LPENUMLOGFONTW :: ^ENUMLOGFONTW
ENUMLOGFONTEXW :: struct {
elfLogFont: LOGFONTW,
elfFullName: [LF_FULLFACESIZE]WCHAR,
elfStyle: [LF_FACESIZE]WCHAR,
elfScript: [LF_FACESIZE]WCHAR,
}
ENUMLOGFONTEXDVW :: struct {
elfEnumLogfontEx: ENUMLOGFONTEXW,
elfDesignVector: DESIGNVECTOR,
}
NEWTEXTMETRICW :: struct {
tmHeight: LONG,
tmAscent: LONG,
tmDescent: LONG,
tmInternalLeading: LONG,
tmExternalLeading: LONG,
tmAveCharWidth: LONG,
tmMaxCharWidth: LONG,
tmWeight: LONG,
tmOverhang: LONG,
tmDigitizedAspectX: LONG,
tmDigitizedAspectY: LONG,
tmFirstChar: WCHAR,
tmLastChar: WCHAR,
tmDefaultChar: WCHAR,
tmBreakChar: WCHAR,
tmItalic: BYTE,
tmUnderlined: BYTE,
tmStruckOut: BYTE,
tmPitchAndFamily: BYTE,
tmCharSet: BYTE,
ntmFlags: DWORD,
ntmSizeEM: UINT,
ntmCellHeight: UINT,
ntmAvgWidth: UINT,
}
FONTENUMPROCW :: #type proc(lpelf: ^ENUMLOGFONTW, lpntm: ^NEWTEXTMETRICW, FontType: DWORD, lParam: LPARAM) -> INT
+33 -14
View File
@@ -51,18 +51,14 @@ foreign kernel32 {
// https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents // https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
GetNumberOfConsoleInputEvents :: proc(hConsoleInput: HANDLE, lpcNumberOfEvents: LPDWORD) -> BOOL --- GetNumberOfConsoleInputEvents :: proc(hConsoleInput: HANDLE, lpcNumberOfEvents: LPDWORD) -> BOOL ---
GetConsoleMode :: proc(hConsoleHandle: HANDLE, GetConsoleMode :: proc(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL ---
lpMode: LPDWORD) -> BOOL --- SetConsoleMode :: proc(hConsoleHandle: HANDLE, dwMode: DWORD) -> BOOL ---
SetConsoleMode :: proc(hConsoleHandle: HANDLE, SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE, dwCursorPosition: COORD) -> BOOL ---
dwMode: DWORD) -> BOOL --- SetConsoleTextAttribute :: proc(hConsoleOutput: HANDLE, wAttributes: WORD) -> BOOL ---
SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE, GetConsoleCP :: proc() -> CODEPAGE ---
dwCursorPosition: COORD) -> BOOL --- SetConsoleCP :: proc(wCodePageID: CODEPAGE) -> BOOL ---
SetConsoleTextAttribute :: proc(hConsoleOutput: HANDLE, GetConsoleOutputCP :: proc() -> CODEPAGE ---
wAttributes: WORD) -> BOOL --- SetConsoleOutputCP :: proc(wCodePageID: CODEPAGE) -> BOOL ---
GetConsoleCP :: proc() -> UINT ---
SetConsoleCP :: proc(wCodePageID: UINT) -> BOOL ---
GetConsoleOutputCP :: proc() -> UINT ---
SetConsoleOutputCP :: proc(wCodePageID: UINT) -> BOOL ---
FlushConsoleInputBuffer :: proc(hConsoleInput: HANDLE) -> BOOL --- FlushConsoleInputBuffer :: proc(hConsoleInput: HANDLE) -> BOOL ---
GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL --- GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL ---
@@ -256,6 +252,7 @@ foreign kernel32 {
SetEnvironmentVariableW :: proc(n: LPCWSTR, v: LPCWSTR) -> BOOL --- SetEnvironmentVariableW :: proc(n: LPCWSTR, v: LPCWSTR) -> BOOL ---
GetEnvironmentStringsW :: proc() -> LPWCH --- GetEnvironmentStringsW :: proc() -> LPWCH ---
FreeEnvironmentStringsW :: proc(env_ptr: LPWCH) -> BOOL --- FreeEnvironmentStringsW :: proc(env_ptr: LPWCH) -> BOOL ---
ExpandEnvironmentStringsW :: proc(lpSrc: LPCWSTR, lpDst: LPWSTR, nSize: DWORD) -> DWORD ---
GetModuleFileNameW :: proc(hModule: HMODULE, lpFilename: LPWSTR, nSize: DWORD) -> DWORD --- GetModuleFileNameW :: proc(hModule: HMODULE, lpFilename: LPWSTR, nSize: DWORD) -> DWORD ---
CreateDirectoryW :: proc( CreateDirectoryW :: proc(
lpPathName: LPCWSTR, lpPathName: LPCWSTR,
@@ -440,12 +437,34 @@ foreign kernel32 {
GetFileAttributesExW :: proc(lpFileName: LPCWSTR, fInfoLevelId: GET_FILEEX_INFO_LEVELS, lpFileInformation: LPVOID) -> BOOL --- GetFileAttributesExW :: proc(lpFileName: LPCWSTR, fInfoLevelId: GET_FILEEX_INFO_LEVELS, lpFileInformation: LPVOID) -> BOOL ---
GetSystemInfo :: proc(system_info: ^SYSTEM_INFO) --- GetSystemInfo :: proc(system_info: ^SYSTEM_INFO) ---
GetVersionExW :: proc(osvi: ^OSVERSIONINFOEXW) --- GetVersionExW :: proc(osvi: ^OSVERSIONINFOEXW) ---
GetSystemDirectoryW :: proc(lpBuffer: LPWSTR, uSize: UINT) -> UINT ---
GetWindowsDirectoryW :: proc(lpBuffer: LPWSTR, uSize: UINT) -> UINT ---
GetSystemDefaultLangID :: proc() -> LANGID ---
GetSystemDefaultLCID :: proc() -> LCID ---
GetSystemDefaultLocaleName :: proc(lpLocaleName: LPWSTR, cchLocaleName: INT) -> INT ---
LCIDToLocaleName :: proc(Locale: LCID, lpName: LPWSTR, cchName: INT, dwFlags: DWORD) -> INT ---
LocaleNameToLCID :: proc(lpName: LPCWSTR, dwFlags: DWORD) -> LCID ---
SetDllDirectoryW :: proc(lpPathName: LPCWSTR) -> BOOL ---
AddDllDirectory :: proc(NewDirectory: PCWSTR) -> rawptr ---
RemoveDllDirectory :: proc(Cookie: rawptr) -> BOOL ---
LoadLibraryW :: proc(c_str: LPCWSTR) -> HMODULE --- LoadLibraryW :: proc(c_str: LPCWSTR) -> HMODULE ---
LoadLibraryExW :: proc(c_str: LPCWSTR, hFile: HANDLE, dwFlags: LoadLibraryEx_Flags) -> HMODULE ---
FreeLibrary :: proc(h: HMODULE) -> BOOL --- FreeLibrary :: proc(h: HMODULE) -> BOOL ---
GetProcAddress :: proc(h: HMODULE, c_str: LPCSTR) -> rawptr --- GetProcAddress :: proc(h: HMODULE, c_str: LPCSTR) -> rawptr ---
LoadLibraryExW :: proc(c_str: LPCWSTR, file: HANDLE, flags: LoadLibraryEx_Flags) -> HMODULE ---
LoadResource :: proc(hModule: HMODULE, hResInfo: HRSRC) -> HGLOBAL ---
FreeResource :: proc(hResData: HGLOBAL) -> BOOL ---
LockResource :: proc(hResData: HGLOBAL) -> LPVOID ---
SizeofResource :: proc(hModule: HMODULE, hResInfo: HRSRC) -> DWORD ---
FindResourceW :: proc(hModule: HMODULE, lpName: LPCWSTR, lpType: LPCWSTR) -> HRSRC ---
FindResourceExW :: proc(hModule: HMODULE, lpType: LPCWSTR, lpName: LPCWSTR, wLanguage: LANGID) -> HRSRC ---
EnumResourceNamesW :: proc(hModule: HMODULE, lpType: LPCWSTR, lpEnumFunc: ENUMRESNAMEPROCW, lParam: LONG_PTR) -> BOOL ---
EnumResourceNamesExW :: proc(hModule: HMODULE, lpType: LPCWSTR, lpEnumFunc: ENUMRESNAMEPROCW, lParam: LONG_PTR, dwFlags: DWORD, LangId: LANGID) -> BOOL ---
EnumResourceTypesExW :: proc(hModule: HMODULE, lpEnumFunc: ENUMRESTYPEPROCW, lParam: LONG_PTR, dwFlags: DWORD, LangId: LANGID) -> BOOL ---
EnumResourceLanguagesExW :: proc(hModule: HMODULE, lpType: LPCWSTR, lpName: LPCWSTR, lpEnumFunc: ENUMRESLANGPROCW, lParam: LONG_PTR, dwFlags: DWORD, LangId: LANGID) -> BOOL ---
LookupIconIdFromDirectory :: proc(presbits: PBYTE, fIcon: BOOL) -> INT ---
LookupIconIdFromDirectoryEx :: proc(presbits: PBYTE, fIcon: BOOL, cxDesired: INT, cyDesired: INT, Flags: UINT) -> INT ---
CreateIconFromResourceEx :: proc(presbits: PBYTE, dwResSize: DWORD, fIcon: BOOL, dwVer: DWORD, cxDesired: INT, cyDesired: INT, Flags: UINT) -> HICON ---
GetFullPathNameW :: proc(filename: LPCWSTR, buffer_length: DWORD, buffer: LPCWSTR, file_part: ^LPCWSTR) -> DWORD --- GetFullPathNameW :: proc(filename: LPCWSTR, buffer_length: DWORD, buffer: LPCWSTR, file_part: ^LPCWSTR) -> DWORD ---
GetLongPathNameW :: proc(short, long: LPCWSTR, len: DWORD) -> DWORD --- GetLongPathNameW :: proc(short, long: LPCWSTR, len: DWORD) -> DWORD ---
+38
View File
@@ -23,8 +23,24 @@ foreign ntdll_lib {
Length: ULONG, Length: ULONG,
FileInformationClass: FILE_INFORMATION_CLASS, FileInformationClass: FILE_INFORMATION_CLASS,
) -> NTSTATUS --- ) -> NTSTATUS ---
NtQueryDirectoryFileEx :: proc(
FileHandle: HANDLE,
Event: HANDLE,
ApcRoutine: PIO_APC_ROUTINE,
ApcContext: PVOID,
IoStatusBlock: PIO_STATUS_BLOCK,
FileInformation: PVOID,
Length: ULONG,
FileInformationClass: FILE_INFORMATION_CLASS,
QueryFlags: ULONG,
FileName : PUNICODE_STRING,
) -> NTSTATUS ---
} }
PIO_APC_ROUTINE :: #type proc "system" (ApcContext: rawptr, IoStatusBlock: PIO_STATUS_BLOCK, Reserved: ULONG)
PIO_STATUS_BLOCK :: ^IO_STATUS_BLOCK PIO_STATUS_BLOCK :: ^IO_STATUS_BLOCK
IO_STATUS_BLOCK :: struct { IO_STATUS_BLOCK :: struct {
using _: struct #raw_union { using _: struct #raw_union {
@@ -45,6 +61,12 @@ PROCESS_INFO_CLASS :: enum c_int {
ProcessSubsystemInformation = 75, ProcessSubsystemInformation = 75,
} }
SL_RESTART_SCAN :: 0x00000001 // The scan will start at the first entry in the directory. If this flag is not set, the scan will resume from where the last query ended.
SL_RETURN_SINGLE_ENTRY :: 0x00000002 // Normally the return buffer is packed with as many matching directory entries that fit. If this flag is set, the file system will return only one directory entry at a time. This does make the operation less efficient.
SL_INDEX_SPECIFIED :: 0x00000004 // The scan should start at a specified indexed position in the directory. This flag can only be set if you generate your own IRP_MJ_DIRECTORY_CONTROL IRP; the index is specified in the IRP. How the position is specified varies from file system to file system.
SL_RETURN_ON_DISK_ENTRIES_ONLY :: 0x00000008 // Any file system filters that perform directory virtualization or just-in-time expansion should simply pass the request through to the file system and return entries that are currently on disk. Not all file systems support this flag.
SL_NO_CURSOR_UPDATE_QUERY :: 0x00000010 // File systems maintain per-FileObject directory cursor information. When multiple threads do queries using the same FileObject, access to the per-FileObject structure is single threaded to prevent corruption of the cursor state. This flag tells the file system to not update per-FileObject cursor state information thus allowing multiple threads to query in parallel using the same handle. It behaves as if SL_RESTART_SCAN is specified on each call. If a wild card pattern is given on the next call, the operation will not pick up where the last query ended. This allows for true asynchronous directory query support. If this flag is used inside a TxF transaction the operation will be failed. Not all file systems support this flag.
PFILE_INFORMATION_CLASS :: ^FILE_INFORMATION_CLASS PFILE_INFORMATION_CLASS :: ^FILE_INFORMATION_CLASS
FILE_INFORMATION_CLASS :: enum c_int { FILE_INFORMATION_CLASS :: enum c_int {
@@ -134,6 +156,22 @@ FILE_INFORMATION_CLASS :: enum c_int {
FileMaximumInformation, FileMaximumInformation,
} }
PFILE_ID_FULL_DIR_INFORMATION :: ^FILE_ID_FULL_DIR_INFORMATION
FILE_ID_FULL_DIR_INFORMATION :: struct {
NextEntryOffset: ULONG,
FileIndex: ULONG,
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
AllocationSize: LARGE_INTEGER,
FileAttributes: ULONG,
FileNameLength: ULONG,
EaSize: ULONG,
FileId: LARGE_INTEGER,
FileName: [1]WCHAR,
}
PROCESS_BASIC_INFORMATION :: struct { PROCESS_BASIC_INFORMATION :: struct {
+15 -1
View File
@@ -40,7 +40,8 @@ LPUNKNOWN :: ^IUnknown
@(default_calling_convention="system") @(default_calling_convention="system")
foreign Ole32 { foreign Ole32 {
CoInitializeEx :: proc(reserved: rawptr, co_init: COINIT) -> HRESULT --- CoInitialize :: proc(reserved: rawptr = nil) -> HRESULT ---
CoInitializeEx :: proc(reserved: rawptr = nil, co_init: COINIT = .APARTMENTTHREADED) -> HRESULT ---
CoUninitialize :: proc() --- CoUninitialize :: proc() ---
CoCreateInstance :: proc( CoCreateInstance :: proc(
@@ -52,4 +53,17 @@ foreign Ole32 {
) -> HRESULT --- ) -> HRESULT ---
CoTaskMemFree :: proc(pv: rawptr) --- CoTaskMemFree :: proc(pv: rawptr) ---
CLSIDFromProgID :: proc(lpszProgID: LPCOLESTR, lpclsid: LPCLSID) -> HRESULT ---
CLSIDFromProgIDEx :: proc(lpszProgID, LPCOLESTR, lpclsid: LPCLSID) -> HRESULT ---
CLSIDFromString :: proc(lpsz: LPOLESTR, pclsid: LPCLSID) -> HRESULT ---
IIDFromString :: proc(lpsz: LPOLESTR, lpiid: LPIID) -> HRESULT ---
ProgIDFromCLSID :: proc(clsid: REFCLSID, lplpszProgID: ^LPOLESTR) -> HRESULT ---
StringFromCLSID :: proc(rclsid: REFCLSID, lplpsz: ^LPOLESTR) -> HRESULT ---
StringFromGUID2 :: proc(rclsid: REFCLSID, lplpsz: LPOLESTR, cchMax: INT) -> INT ---
StringFromIID :: proc(rclsid: REFIID, lplpsz: ^LPOLESTR) -> HRESULT ---
PropVariantClear :: proc(pvar: ^PROPVARIANT) -> HRESULT ---
PropVariantCopy :: proc(pvarDest: ^PROPVARIANT, pvarSrc: ^PROPVARIANT) -> HRESULT ---
FreePropVariantArray :: proc(cVariants: ULONG, rgvars: ^PROPVARIANT) -> HRESULT ---
} }
+25
View File
@@ -0,0 +1,25 @@
// +build windows
package sys_windows
foreign import shcore "system:Shcore.lib"
@(default_calling_convention="system")
foreign shcore {
GetProcessDpiAwareness :: proc(hprocess: HANDLE, value: ^PROCESS_DPI_AWARENESS) -> HRESULT ---
SetProcessDpiAwareness :: proc(value: PROCESS_DPI_AWARENESS) -> HRESULT ---
GetDpiForMonitor :: proc(hmonitor: HMONITOR, dpiType: MONITOR_DPI_TYPE, dpiX: ^UINT, dpiY: ^UINT) -> HRESULT ---
}
PROCESS_DPI_AWARENESS :: enum DWORD {
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2,
}
MONITOR_DPI_TYPE :: enum DWORD {
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
MDT_RAW_DPI = 2,
MDT_DEFAULT,
}
+1
View File
@@ -31,6 +31,7 @@ foreign shell32 {
SHSetKnownFolderPath :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, pszPath: PCWSTR ) -> HRESULT --- SHSetKnownFolderPath :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, pszPath: PCWSTR ) -> HRESULT ---
SHGetKnownFolderPath :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, ppszPath: ^LPWSTR) -> HRESULT --- SHGetKnownFolderPath :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, ppszPath: ^LPWSTR) -> HRESULT ---
ExtractIconExW :: proc(pszFile: LPCWSTR, nIconIndex: INT, phiconLarge: ^HICON, phiconSmall: ^HICON, nIcons: UINT) -> UINT ---
DragAcceptFiles :: proc(hWnd: HWND, fAccept: BOOL) --- DragAcceptFiles :: proc(hWnd: HWND, fAccept: BOOL) ---
DragQueryPoint :: proc(hDrop: HDROP, ppt: ^POINT) -> BOOL --- DragQueryPoint :: proc(hDrop: HDROP, ppt: ^POINT) -> BOOL ---
DragQueryFileW :: proc(hDrop: HDROP, iFile: UINT, lpszFile: LPWSTR, cch: UINT) -> UINT --- DragQueryFileW :: proc(hDrop: HDROP, iFile: UINT, lpszFile: LPWSTR, cch: UINT) -> UINT ---
+121 -4
View File
@@ -30,8 +30,10 @@ HICON :: distinct HANDLE
HCURSOR :: distinct HANDLE HCURSOR :: distinct HANDLE
HMENU :: distinct HANDLE HMENU :: distinct HANDLE
HBRUSH :: distinct HANDLE HBRUSH :: distinct HANDLE
HPEN :: distinct HANDLE
HGDIOBJ :: distinct HANDLE HGDIOBJ :: distinct HANDLE
HBITMAP :: distinct HANDLE HBITMAP :: distinct HANDLE
HPALETTE :: distinct HANDLE
HGLOBAL :: distinct HANDLE HGLOBAL :: distinct HANDLE
HHOOK :: distinct HANDLE HHOOK :: distinct HANDLE
HWINEVENTHOOK :: distinct HANDLE HWINEVENTHOOK :: distinct HANDLE
@@ -39,6 +41,9 @@ HKEY :: distinct HANDLE
HDESK :: distinct HANDLE HDESK :: distinct HANDLE
HFONT :: distinct HANDLE HFONT :: distinct HANDLE
HRGN :: distinct HANDLE HRGN :: distinct HANDLE
HRSRC :: distinct HANDLE
HWINSTA :: distinct HANDLE
HACCEL :: distinct HANDLE
BOOL :: distinct b32 BOOL :: distinct b32
BYTE :: distinct u8 BYTE :: distinct u8
BOOLEAN :: distinct b8 BOOLEAN :: distinct b8
@@ -139,11 +144,14 @@ LPSTR :: ^CHAR
LPWSTR :: ^WCHAR LPWSTR :: ^WCHAR
OLECHAR :: WCHAR OLECHAR :: WCHAR
LPOLESTR :: ^OLECHAR LPOLESTR :: ^OLECHAR
LPCOLESTR :: LPCSTR
LPFILETIME :: ^FILETIME LPFILETIME :: ^FILETIME
LPWSABUF :: ^WSABUF LPWSABUF :: ^WSABUF
LPWSAOVERLAPPED :: distinct rawptr LPWSAOVERLAPPED :: distinct rawptr
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
LPCVOID :: rawptr LPCVOID :: rawptr
SCODE :: LONG
PSCODE :: ^SCODE
PACCESS_TOKEN :: PVOID PACCESS_TOKEN :: PVOID
PSECURITY_DESCRIPTOR :: PVOID PSECURITY_DESCRIPTOR :: PVOID
@@ -699,6 +707,92 @@ FW_BLACK :: FW_HEAVY
PTIMERAPCROUTINE :: #type proc "system" (lpArgToCompletionRoutine: LPVOID, dwTimerLowValue, dwTimerHighValue: DWORD) PTIMERAPCROUTINE :: #type proc "system" (lpArgToCompletionRoutine: LPVOID, dwTimerLowValue, dwTimerHighValue: DWORD)
// Character Sets
ANSI_CHARSET :: 0
DEFAULT_CHARSET :: 1
SYMBOL_CHARSET :: 2
SHIFTJIS_CHARSET :: 128
HANGEUL_CHARSET :: 129
HANGUL_CHARSET :: 129
GB2312_CHARSET :: 134
CHINESEBIG5_CHARSET :: 136
OEM_CHARSET :: 255
JOHAB_CHARSET :: 130
HEBREW_CHARSET :: 177
ARABIC_CHARSET :: 178
GREEK_CHARSET :: 161
TURKISH_CHARSET :: 162
VIETNAMESE_CHARSET :: 163
THAI_CHARSET :: 222
EASTEUROPE_CHARSET :: 238
RUSSIAN_CHARSET :: 204
MAC_CHARSET :: 77
BALTIC_CHARSET :: 186
// Font Signature Bitmaps
FS_LATIN1 :: 0x00000001
FS_LATIN2 :: 0x00000002
FS_CYRILLIC :: 0x00000004
FS_GREEK :: 0x00000008
FS_TURKISH :: 0x00000010
FS_HEBREW :: 0x00000020
FS_ARABIC :: 0x00000040
FS_BALTIC :: 0x00000080
FS_VIETNAMESE :: 0x00000100
FS_THAI :: 0x00010000
FS_JISJAPAN :: 0x00020000
FS_CHINESESIMP :: 0x00040000
FS_WANSUNG :: 0x00080000
FS_CHINESETRAD :: 0x00100000
FS_JOHAB :: 0x00200000
FS_SYMBOL :: 0x80000000
// Output Precisions
OUT_DEFAULT_PRECIS :: 0
OUT_STRING_PRECIS :: 1
OUT_CHARACTER_PRECIS :: 2
OUT_STROKE_PRECIS :: 3
OUT_TT_PRECIS :: 4
OUT_DEVICE_PRECIS :: 5
OUT_RASTER_PRECIS :: 6
OUT_TT_ONLY_PRECIS :: 7
OUT_OUTLINE_PRECIS :: 8
OUT_SCREEN_OUTLINE_PRECIS :: 9
OUT_PS_ONLY_PRECIS :: 10
// Clipping Precisions
CLIP_DEFAULT_PRECIS :: 0
CLIP_CHARACTER_PRECIS :: 1
CLIP_STROKE_PRECIS :: 2
CLIP_MASK :: 0xf
CLIP_LH_ANGLES :: 1 << 4
CLIP_TT_ALWAYS :: 2 << 4
CLIP_DFA_DISABLE :: 4 << 4
CLIP_EMBEDDED :: 8 << 4
// Output Qualities
DEFAULT_QUALITY :: 0
DRAFT_QUALITY :: 1
PROOF_QUALITY :: 2
NONANTIALIASED_QUALITY :: 3
ANTIALIASED_QUALITY :: 4
CLEARTYPE_QUALITY :: 5
CLEARTYPE_NATURAL_QUALITY :: 6
// Font Pitches
DEFAULT_PITCH :: 0
FIXED_PITCH :: 1
VARIABLE_PITCH :: 2
MONO_FONT :: 8
// Font Families
FF_DONTCARE :: 0 << 4
FF_ROMAN :: 1 << 4
FF_SWISS :: 2 << 4
FF_MODERN :: 3 << 4
FF_SCRIPT :: 4 << 4
FF_DECORATIVE :: 5 << 4
TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD) TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD)
WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
@@ -932,6 +1026,16 @@ MF_RIGHTJUSTIFY :: 0x00004000
MF_MOUSESELECT :: 0x00008000 MF_MOUSESELECT :: 0x00008000
MF_END :: 0x00000080 // Obsolete -- only used by old RES files MF_END :: 0x00000080 // Obsolete -- only used by old RES files
// Menu flags for Add/Check/EnableMenuItem()
MFS_GRAYED :: 0x00000003
MFS_DISABLED :: MFS_GRAYED
MFS_CHECKED :: MF_CHECKED
MFS_HILITE :: MF_HILITE
MFS_ENABLED :: MF_ENABLED
MFS_UNCHECKED :: MF_UNCHECKED
MFS_UNHILITE :: MF_UNHILITE
MFS_DEFAULT :: MF_DEFAULT
// Flags for TrackPopupMenu // Flags for TrackPopupMenu
TPM_LEFTBUTTON :: 0x0000 TPM_LEFTBUTTON :: 0x0000
TPM_RIGHTBUTTON :: 0x0002 TPM_RIGHTBUTTON :: 0x0002
@@ -1037,9 +1141,6 @@ WIN32_FIND_DATAW :: struct {
dwReserved1: DWORD, dwReserved1: DWORD,
cFileName: [MAX_PATH]WCHAR, cFileName: [MAX_PATH]WCHAR,
cAlternateFileName: [14]WCHAR, cAlternateFileName: [14]WCHAR,
_OBSOLETE_dwFileType: DWORD, // Obsolete. Do not use.
_OBSOLETE_dwCreatorType: DWORD, // Obsolete. Do not use
_OBSOLETE_wFinderFlags: WORD, // Obsolete. Do not use
} }
FILE_ID_128 :: struct { FILE_ID_128 :: struct {
@@ -2207,6 +2308,14 @@ CP_SYMBOL :: 42 // SYMBOL translations
CP_UTF7 :: 65000 // UTF-7 translation CP_UTF7 :: 65000 // UTF-7 translation
CP_UTF8 :: 65001 // UTF-8 translation CP_UTF8 :: 65001 // UTF-8 translation
LCID :: DWORD
LANGID :: WORD
LANG_NEUTRAL :: 0x00
LANG_INVARIANT :: 0x7f
SUBLANG_NEUTRAL :: 0x00 // language neutral
SUBLANG_DEFAULT :: 0x01 // user default
MB_ERR_INVALID_CHARS :: 8 MB_ERR_INVALID_CHARS :: 8
WC_ERR_INVALID_CHARS :: 128 WC_ERR_INVALID_CHARS :: 128
@@ -2422,8 +2531,10 @@ REFIID :: ^GUID
REFGUID :: GUID REFGUID :: GUID
IID :: GUID IID :: GUID
LPIID :: ^IID
CLSID :: GUID CLSID :: GUID
REFCLSID :: ^CLSID REFCLSID :: ^CLSID
LPCLSID :: ^CLSID
CLSCTX_INPROC_SERVER :: 0x1 CLSCTX_INPROC_SERVER :: 0x1
CLSCTX_INPROC_HANDLER :: 0x2 CLSCTX_INPROC_HANDLER :: 0x2
@@ -2453,6 +2564,7 @@ CLSCTX_RESERVED6 :: 0x1000000
CLSCTX_ACTIVATE_ARM32_SERVER :: 0x2000000 CLSCTX_ACTIVATE_ARM32_SERVER :: 0x2000000
CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION :: 0x4000000 CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION :: 0x4000000
CLSCTX_PS_DLL :: 0x80000000 CLSCTX_PS_DLL :: 0x80000000
CLSCTX_ALL :: CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
WSAPROTOCOLCHAIN :: struct { WSAPROTOCOLCHAIN :: struct {
ChainLen: c_int, ChainLen: c_int,
@@ -2512,6 +2624,7 @@ OBJECT_ATTRIBUTES :: struct {
SecurityQualityOfService: rawptr, SecurityQualityOfService: rawptr,
} }
PUNICODE_STRING :: ^UNICODE_STRING
UNICODE_STRING :: struct { UNICODE_STRING :: struct {
Length: u16 `fmt:"-"`, Length: u16 `fmt:"-"`,
MaximumLength: u16 `fmt:"-"`, MaximumLength: u16 `fmt:"-"`,
@@ -4389,7 +4502,7 @@ DNS_STATUS :: distinct DWORD // zero is success
DNS_INFO_NO_RECORDS :: 9501 DNS_INFO_NO_RECORDS :: 9501
DNS_QUERY_NO_RECURSION :: 0x00000004 DNS_QUERY_NO_RECURSION :: 0x00000004
DNS_RECORD :: struct { DNS_RECORD :: struct { // aka DNS_RECORDA
pNext: ^DNS_RECORD, pNext: ^DNS_RECORD,
pName: cstring, pName: cstring,
wType: WORD, wType: WORD,
@@ -4433,6 +4546,10 @@ SOCKADDR :: struct {
sa_data: [14]CHAR, sa_data: [14]CHAR,
} }
ENUMRESNAMEPROCW :: #type proc (hModule: HMODULE, lpType: LPCWSTR, lpName: LPWSTR, lParam: LONG_PTR)-> BOOL
ENUMRESTYPEPROCW :: #type proc (hModule: HMODULE, lpType: LPCWSTR, lParam: LONG_PTR)-> BOOL
ENUMRESLANGPROCW :: #type proc (hModule: HMODULE, lpType: LPCWSTR, lpName: LPWSTR, wIDLanguage: LANGID, lParam: LONG_PTR)-> BOOL
DTR_Control :: enum byte { DTR_Control :: enum byte {
Disable = 0, Disable = 0,
Enable = 1, Enable = 1,
+211 -65
View File
@@ -6,19 +6,18 @@ foreign import user32 "system:User32.lib"
@(default_calling_convention="system") @(default_calling_convention="system")
foreign user32 { foreign user32 {
GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL --- GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassName: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL --- GetClassInfoExW :: proc(hInstance: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD --- GetClassLongW :: proc(hWnd: HWND, nIndex: INT) -> DWORD ---
SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD --- SetClassLongW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG) -> DWORD ---
GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG --- GetWindowLongW :: proc(hWnd: HWND, nIndex: INT) -> LONG ---
SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG --- SetWindowLongW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG) -> LONG ---
GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int --- GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: INT) -> INT ---
GetParent :: proc(hWnd: HWND) -> HWND --- GetParent :: proc(hWnd: HWND) -> HWND ---
IsWindowVisible :: proc(hWnd: HWND) -> BOOL ---
SetWinEventHook :: proc( SetWinEventHook :: proc(
eventMin, eventMax: DWORD, eventMin, eventMax: DWORD,
hmodWinEventProc: HMODULE, hmodWinEventProc: HMODULE,
@@ -38,10 +37,7 @@ foreign user32 {
lpClassName: LPCWSTR, lpClassName: LPCWSTR,
lpWindowName: LPCWSTR, lpWindowName: LPCWSTR,
dwStyle: DWORD, dwStyle: DWORD,
X: c_int, X, Y, nWidth, nHeight: INT,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND, hWndParent: HWND,
hMenu: HMENU, hMenu: HMENU,
hInstance: HINSTANCE, hInstance: HINSTANCE,
@@ -50,24 +46,33 @@ foreign user32 {
DestroyWindow :: proc(hWnd: HWND) -> BOOL --- DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL --- ShowWindow :: proc(hWnd: HWND, nCmdShow: INT) -> BOOL ---
IsWindow :: proc(hWnd: HWND) -> BOOL --- IsWindow :: proc(hWnd: HWND) -> BOOL ---
IsWindowVisible :: proc(hwnd: HWND) -> BOOL ---
IsWindowEnabled :: proc(hwnd: HWND) -> BOOL ---
IsIconic :: proc(hwnd: HWND) -> BOOL ---
BringWindowToTop :: proc(hWnd: HWND) -> BOOL --- BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
GetTopWindow :: proc(hWnd: HWND) -> HWND --- GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL --- SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
GetForegroundWindow :: proc() -> HWND --- GetForegroundWindow :: proc() -> HWND ---
GetDesktopWindow :: proc() -> HWND ---
UpdateWindow :: proc(hWnd: HWND) -> BOOL --- UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
SetActiveWindow :: proc(hWnd: HWND) -> HWND --- SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
GetActiveWindow :: proc() -> HWND --- GetActiveWindow :: proc() -> HWND ---
RedrawWindow :: proc(hwnd: HWND, lprcUpdate: LPRECT, hrgnUpdate: HRGN, flags: RedrawWindowFlags) -> BOOL --- RedrawWindow :: proc(hwnd: HWND, lprcUpdate: LPRECT, hrgnUpdate: HRGN, flags: RedrawWindowFlags) -> BOOL ---
SetParent :: proc(hWndChild: HWND, hWndNewParent: HWND) -> HWND ---
SetPropW :: proc(hWnd: HWND, lpString: LPCWSTR, hData: HANDLE) -> BOOL ---
GetPropW :: proc(hWnd: HWND, lpString: LPCWSTR) -> HANDLE ---
RemovePropW :: proc(hWnd: HWND, lpString: LPCWSTR) -> HANDLE ---
EnumPropsW :: proc(hWnd: HWND, lpEnumFunc: PROPENUMPROCW) -> INT ---
EnumPropsExW :: proc(hWnd: HWND, lpEnumFunc: PROPENUMPROCW, lParam: LPARAM) -> INT ---
GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL --- GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL --- TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT --- DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
WaitMessage :: proc() -> BOOL --- WaitMessage :: proc() -> BOOL ---
MsgWaitForMultipleObjects :: proc(nCount: DWORD, pHandles: ^HANDLE, fWaitAll: bool, dwMilliseconds: DWORD, dwWakeMask: DWORD) -> DWORD --- MsgWaitForMultipleObjects :: proc(nCount: DWORD, pHandles: ^HANDLE, fWaitAll: BOOL, dwMilliseconds: DWORD, dwWakeMask: DWORD) -> DWORD ---
PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL --- PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL --- PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
@@ -80,7 +85,7 @@ foreign user32 {
PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL --- PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL --- PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostQuitMessage :: proc(nExitCode: c_int) --- PostQuitMessage :: proc(nExitCode: INT) ---
GetQueueStatus :: proc(flags: UINT) -> DWORD --- GetQueueStatus :: proc(flags: UINT) -> DWORD ---
@@ -94,33 +99,26 @@ foreign user32 {
LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON --- LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON --- LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
GetIconInfoExW :: proc(hIcon: HICON, piconinfo: PICONINFOEXW) -> BOOL ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR --- LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR --- LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE --- LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx, cy: INT, fuLoad: UINT) -> HANDLE ---
CreateIcon :: proc(hInstance: HINSTANCE, nWidth: c_int, nHeight: c_int, cPlanes: BYTE, cBitsPixel: BYTE, lpbANDbits: PBYTE, lpbXORbits: PBYTE) -> HICON --- CreateIcon :: proc(hInstance: HINSTANCE, nWidth, nHeight: INT, cPlanes: BYTE, cBitsPixel: BYTE, lpbANDbits: PBYTE, lpbXORbits: PBYTE) -> HICON ---
CreateIconFromResource :: proc(presbits: PBYTE, dwResSize: DWORD, fIcon: BOOL, dwVer: DWORD) -> HICON --- CreateIconFromResource :: proc(presbits: PBYTE, dwResSize: DWORD, fIcon: BOOL, dwVer: DWORD) -> HICON ---
DestroyIcon :: proc(hIcon: HICON) -> BOOL --- DestroyIcon :: proc(hIcon: HICON) -> BOOL ---
DrawIcon :: proc(hDC: HDC, X: c_int, Y: c_int, hIcon: HICON) -> BOOL --- DrawIcon :: proc(hDC: HDC, X, Y: INT, hIcon: HICON) -> BOOL ---
CreateCursor :: proc(hInst: HINSTANCE, xHotSpot: c_int, yHotSpot: c_int, nWidth: c_int, nHeight: c_int, pvANDPlane: PVOID, pvXORPlane: PVOID) -> HCURSOR --- CreateCursor :: proc(hInst: HINSTANCE, xHotSpot, yHotSpot, nWidth, nHeight: INT, pvANDPlane: PVOID, pvXORPlane: PVOID) -> HCURSOR ---
DestroyCursor :: proc(hCursor: HCURSOR) -> BOOL --- DestroyCursor :: proc(hCursor: HCURSOR) -> BOOL ---
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL --- GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL --- GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL --- ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
ScreenToClient :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL --- ScreenToClient :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
SetWindowPos :: proc( SetWindowPos :: proc(hWnd: HWND, hWndInsertAfter: HWND, X, Y, cx, cy: INT, uFlags: UINT) -> BOOL ---
hWnd: HWND, MoveWindow :: proc(hWnd: HWND, X, Y, hWidth, hHeight: INT, bRepaint: BOOL) -> BOOL ---
hWndInsertAfter: HWND, GetSystemMetrics :: proc(nIndex: INT) -> INT ---
X: c_int,
Y: c_int,
cx: c_int,
cy: c_int,
uFlags: UINT,
) -> BOOL ---
MoveWindow :: proc(hWnd: HWND, X, Y, hWidth, hHeight: c_int, bRepaint: BOOL) -> BOOL ---
GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL --- AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL --- AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL --- AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL ---
@@ -130,18 +128,36 @@ foreign user32 {
GetWindowDC :: proc(hWnd: HWND) -> HDC --- GetWindowDC :: proc(hWnd: HWND) -> HDC ---
GetDC :: proc(hWnd: HWND) -> HDC --- GetDC :: proc(hWnd: HWND) -> HDC ---
ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int --- GetDCEx :: proc(hWnd: HWND, hrgnClip: HRGN, flags: DWORD) -> HDC ---
ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> INT ---
GetDlgCtrlID :: proc(hWnd: HWND) -> c_int --- GetDlgCtrlID :: proc(hWnd: HWND) -> INT ---
GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: c_int) -> HWND --- GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: INT) -> HWND ---
CreateMenu :: proc() -> HMENU ---
CreatePopupMenu :: proc() -> HMENU --- CreatePopupMenu :: proc() -> HMENU ---
DeleteMenu :: proc(hMenu: HMENU, uPosition: UINT, uFlags: UINT) -> BOOL ---
DestroyMenu :: proc(hMenu: HMENU) -> BOOL --- DestroyMenu :: proc(hMenu: HMENU) -> BOOL ---
InsertMenuW :: proc(hMenu: HMENU, uPosition: UINT, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL ---
AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL --- AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL ---
GetMenu :: proc(hWnd: HWND) -> HMENU ---
SetMenu :: proc(hWnd: HWND, hMenu: HMENU) -> BOOL --- SetMenu :: proc(hWnd: HWND, hMenu: HMENU) -> BOOL ---
TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x: int, y: int, nReserved: int, hWnd: HWND, prcRect: ^RECT) -> i32 --- TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x, y: INT, nReserved: INT, hWnd: HWND, prcRect: ^RECT) -> BOOL ---
RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT --- RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT ---
CreateAcceleratorTableW :: proc(paccel: LPACCEL, cAccel: INT) -> HACCEL ---
DestroyAcceleratorTable :: proc(hAccel: HACCEL) -> BOOL ---
LoadAcceleratorsW :: proc(hInstance: HINSTANCE, lpTableName: LPCWSTR) -> HACCEL ---
TranslateAcceleratorW :: proc(hWnd: HWND, hAccTable: HACCEL, lpMsg: LPMSG) -> INT ---
CopyAcceleratorTableW :: proc(hAccelSrc: HACCEL, lpAccelDst: LPACCEL, cAccelEntries: INT) -> INT ---
InsertMenuItemW :: proc(hmenu: HMENU, item: UINT, fByPosition: BOOL, lpmi: LPMENUITEMINFOW) -> BOOL ---
GetMenuItemInfoW :: proc(hmenu: HMENU, item: UINT, fByPosition: BOOL, lpmii: LPMENUITEMINFOW) -> BOOL ---
SetMenuItemInfoW :: proc(hmenu: HMENU, item: UINT, fByPositon: BOOL, lpmii: LPMENUITEMINFOW) -> BOOL ---
GetMenuDefaultItem :: proc(hMenu: HMENU, fByPos: UINT, gmdiFlags: UINT) -> UINT ---
SetMenuDefaultItem :: proc(hMenu: HMENU, uItem: UINT, fByPos: UINT) -> BOOL ---
GetMenuItemRect :: proc(hWnd: HWND, hMenu: HMENU, uItem: UINT, lprcItem: LPRECT) -> c_int ---
GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL --- GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL --- ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL --- InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
@@ -154,34 +170,35 @@ foreign user32 {
ReleaseCapture :: proc() -> BOOL --- ReleaseCapture :: proc() -> BOOL ---
TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL --- TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL ---
GetKeyState :: proc(nVirtKey: c_int) -> SHORT --- GetKeyState :: proc(nVirtKey: INT) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT --- GetAsyncKeyState :: proc(vKey: INT) -> SHORT ---
GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL --- GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT --- MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
ToUnicode :: proc(nVirtKey: UINT, wScanCode: UINT, lpKeyState: ^BYTE, pwszBuff: LPWSTR, cchBuff: c_int, wFlags: UINT) -> c_int --- ToUnicode :: proc(nVirtKey: UINT, wScanCode: UINT, lpKeyState: ^BYTE, pwszBuff: LPWSTR, cchBuff: INT, wFlags: UINT) -> INT ---
SetWindowsHookExW :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK --- SetWindowsHookExW :: proc(idHook: INT, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL --- UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL ---
CallNextHookEx :: proc(hhk: HHOOK, nCode: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- CallNextHookEx :: proc(hhk: HHOOK, nCode: INT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR --- SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR ---
KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL --- KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
// MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int --- // MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> INT ---
MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int --- MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> INT ---
// MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int --- // MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> INT ---
MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int --- MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> INT ---
ClipCursor :: proc(lpRect: LPRECT) -> BOOL --- ClipCursor :: proc(lpRect: LPRECT) -> BOOL ---
GetCursorPos :: proc(lpPoint: LPPOINT) -> BOOL --- GetCursorPos :: proc(lpPoint: LPPOINT) -> BOOL ---
SetCursorPos :: proc(X: c_int, Y: c_int) -> BOOL --- SetCursorPos :: proc(X, Y: INT) -> BOOL ---
SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR --- SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR ---
when !intrinsics.is_package_imported("raylib") { when !intrinsics.is_package_imported("raylib") {
ShowCursor :: proc(bShow: BOOL) -> INT --- ShowCursor :: proc(bShow: BOOL) -> INT ---
} }
EnumDisplayDevicesW :: proc (lpDevice: LPCWSTR, iDevNum: DWORD, lpDisplayDevice: PDISPLAY_DEVICEW, dwFlags: DWORD) -> BOOL ---
EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL --- EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL ---
MonitorFromPoint :: proc(pt: POINT, dwFlags: Monitor_From_Flags) -> HMONITOR --- MonitorFromPoint :: proc(pt: POINT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
@@ -191,6 +208,9 @@ foreign user32 {
EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL --- EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
IsProcessDPIAware :: proc() -> BOOL ---
SetProcessDPIAware :: proc() -> BOOL ---
SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT --- SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT ---
GetThreadDpiAwarenessContext :: proc() -> DPI_AWARENESS_CONTEXT --- GetThreadDpiAwarenessContext :: proc() -> DPI_AWARENESS_CONTEXT ---
GetWindowDpiAwarenessContext :: proc(hwnd: HWND) -> DPI_AWARENESS_CONTEXT --- GetWindowDpiAwarenessContext :: proc(hwnd: HWND) -> DPI_AWARENESS_CONTEXT ---
@@ -225,14 +245,14 @@ foreign user32 {
lpdwResult: PDWORD_PTR, lpdwResult: PDWORD_PTR,
) -> LRESULT --- ) -> LRESULT ---
GetSysColor :: proc(nIndex: c_int) -> DWORD --- GetSysColor :: proc(nIndex: INT) -> DWORD ---
GetSysColorBrush :: proc(nIndex: c_int) -> HBRUSH --- GetSysColorBrush :: proc(nIndex: INT) -> HBRUSH ---
SetSysColors :: proc(cElements: c_int, lpaElements: ^INT, lpaRgbValues: ^COLORREF) -> BOOL --- SetSysColors :: proc(cElements: INT, lpaElements: ^INT, lpaRgbValues: ^COLORREF) -> BOOL ---
MessageBeep :: proc(uType: UINT) -> BOOL --- MessageBeep :: proc(uType: UINT) -> BOOL ---
IsDialogMessageW :: proc(hDlg: HWND, lpMsg: LPMSG) -> BOOL --- IsDialogMessageW :: proc(hDlg: HWND, lpMsg: LPMSG) -> BOOL ---
GetWindowTextLengthW :: proc(hWnd: HWND) -> c_int --- GetWindowTextLengthW :: proc(hWnd: HWND) -> INT ---
GetWindowTextW :: proc(hWnd: HWND, lpString: LPWSTR, nMaxCount: c_int) -> c_int --- GetWindowTextW :: proc(hWnd: HWND, lpString: LPWSTR, nMaxCount: INT) -> INT ---
SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL --- SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL ---
CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL --- EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL ---
@@ -245,12 +265,20 @@ foreign user32 {
GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT --- GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL --- RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
SendInput :: proc(cInputs: UINT, pInputs: [^]INPUT, cbSize: c_int) -> UINT --- SendInput :: proc(cInputs: UINT, pInputs: [^]INPUT, cbSize: INT) -> UINT ---
SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL --- SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL ---
FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int --- FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int ---
EqualRect :: proc(lprc1: ^RECT, lprc2: ^RECT) -> BOOL --- EqualRect :: proc(lprc1, lprc2: ^RECT) -> BOOL ---
OffsetRect :: proc(lprc1: ^RECT, dx, dy: INT) -> BOOL ---
InflateRect :: proc(lprc1: ^RECT, dx, dy: INT) -> BOOL ---
IntersectRect :: proc(lprcDst, lprcSrc1, lprcSrc2: ^RECT) -> BOOL ---
SubtractRect :: proc(lprcDst, lprcSrc1, lprcSrc2: ^RECT) -> BOOL ---
UnionRect :: proc(lprcDst, lprcSrc1, lprcSrc2: ^RECT) -> BOOL ---
IsRectEmpty :: proc(lprc: ^RECT) -> BOOL ---
SetRectEmpty :: proc(lprc: ^RECT) -> BOOL ---
CopyRect :: proc(lprcDst, lprcSrc: ^RECT) -> BOOL ---
GetWindowInfo :: proc(hwnd: HWND, pwi: PWINDOWINFO) -> BOOL --- GetWindowInfo :: proc(hwnd: HWND, pwi: PWINDOWINFO) -> BOOL ---
GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL --- GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
@@ -259,21 +287,34 @@ foreign user32 {
CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN --- CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int --- GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
GetCursorInfo :: proc(pci: PCURSORINFO) -> BOOL ---
GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU --- GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU ---
EnableMenuItem :: proc(hMenu: HMENU, uIDEnableItem: UINT, uEnable: UINT) -> BOOL --- EnableMenuItem :: proc(hMenu: HMENU, uIDEnableItem: UINT, uEnable: UINT) -> BOOL ---
MenuItemFromPoint :: proc(hWnd: HWND, hMenu: HMENU, ptScreen: POINT) -> INT ---
DrawTextW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat) -> INT --- DrawTextW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat) -> INT ---
DrawTextExW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat, lpdtp: PDRAWTEXTPARAMS) -> INT --- DrawTextExW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat, lpdtp: PDRAWTEXTPARAMS) -> INT ---
GetLocaleInfoEx :: proc(lpLocaleName: LPCWSTR, LCType: LCTYPE, lpLCData: LPWSTR, cchData: INT) -> INT ---
IsValidLocaleName :: proc(lpLocaleName: LPCWSTR) -> BOOL ---
ResolveLocaleName :: proc(lpNameToResolve: LPCWSTR, lpLocaleName: LPWSTR, cchLocaleName: INT) -> INT ---
IsValidCodePage :: proc(CodePage: UINT) -> BOOL ---
GetACP :: proc() -> CODEPAGE ---
GetCPInfoExW :: proc(CodePage: CODEPAGE, dwFlags: DWORD, lpCPInfoEx: LPCPINFOEXW) -> BOOL ---
GetProcessWindowStation :: proc() -> HWINSTA ---
GetUserObjectInformationW :: proc(hObj: HANDLE, nIndex: GetUserObjectInformationFlags, pvInfo: PVOID, nLength: DWORD, lpnLengthNeeded: LPDWORD) -> BOOL ---
} }
CreateWindowW :: #force_inline proc "system" ( CreateWindowW :: #force_inline proc "system" (
lpClassName: LPCTSTR, lpClassName: LPCTSTR,
lpWindowName: LPCTSTR, lpWindowName: LPCTSTR,
dwStyle: DWORD, dwStyle: DWORD,
X: c_int, X: INT,
Y: c_int, Y: INT,
nWidth: c_int, nWidth: INT,
nHeight: c_int, nHeight: INT,
hWndParent: HWND, hWndParent: HWND,
hMenu: HMENU, hMenu: HMENU,
hInstance: HINSTANCE, hInstance: HINSTANCE,
@@ -298,11 +339,11 @@ CreateWindowW :: #force_inline proc "system" (
when ODIN_ARCH == .amd64 { when ODIN_ARCH == .amd64 {
@(default_calling_convention="system") @(default_calling_convention="system")
foreign user32 { foreign user32 {
GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR --- GetClassLongPtrW :: proc(hWnd: HWND, nIndex: INT) -> ULONG_PTR ---
SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR --- SetClassLongPtrW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG_PTR) -> ULONG_PTR ---
GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR --- GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: INT) -> LONG_PTR ---
SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR --- SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG_PTR) -> LONG_PTR ---
} }
} else when ODIN_ARCH == .i386 { } else when ODIN_ARCH == .i386 {
GetClassLongPtrW :: GetClassLongW GetClassLongPtrW :: GetClassLongW
@@ -312,8 +353,8 @@ when ODIN_ARCH == .amd64 {
SetWindowLongPtrW :: SetWindowLongW SetWindowLongPtrW :: SetWindowLongW
} }
GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int { GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> INT {
return c_int(wParam) & 0xFFF0 return INT(wParam) & 0xFFF0
} }
GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short { GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
@@ -332,6 +373,10 @@ GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD
return HIWORD(cast(DWORD)wParam) return HIWORD(cast(DWORD)wParam)
} }
GET_RAWINPUT_CODE_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> BYTE {
return BYTE(wParam) & 0xFF
}
MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR { MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR {
return cast(LPWSTR)uintptr(WORD(i)) return cast(LPWSTR)uintptr(WORD(i))
} }
@@ -530,11 +575,20 @@ WINDOWINFO :: struct {
} }
PWINDOWINFO :: ^WINDOWINFO PWINDOWINFO :: ^WINDOWINFO
CURSORINFO :: struct {
cbSize: DWORD,
flags: DWORD,
hCursor: HCURSOR,
ptScreenPos: POINT,
}
PCURSORINFO :: ^CURSORINFO
DRAWTEXTPARAMS :: struct { DRAWTEXTPARAMS :: struct {
cbSize: UINT, cbSize: UINT,
iTabLength: int, iTabLength: INT,
iLeftMargin: int, iLeftMargin: INT,
iRightMargin: int, iRightMargin: INT,
uiLengthDrawn: UINT, uiLengthDrawn: UINT,
} }
PDRAWTEXTPARAMS :: ^DRAWTEXTPARAMS PDRAWTEXTPARAMS :: ^DRAWTEXTPARAMS
@@ -581,6 +635,98 @@ RedrawWindowFlags :: enum UINT {
RDW_NOFRAME = 0x0800, RDW_NOFRAME = 0x0800,
} }
GetUserObjectInformationFlags :: enum INT {
UOI_FLAGS = 1,
UOI_NAME = 2,
UOI_TYPE = 3,
UOI_USER_SID = 4,
UOI_HEAPSIZE = 5,
UOI_IO = 6,
UOI_TIMERPROC_EXCEPTION_SUPPRESSION = 7,
}
USEROBJECTFLAGS :: struct {
fInherit: BOOL,
fReserved: BOOL,
dwFlags: DWORD,
}
PROPENUMPROCW :: #type proc(unnamedParam1: HWND, unnamedParam2: LPCWSTR, unnamedParam3: HANDLE) -> BOOL
PROPENUMPROCEXW :: #type proc(unnamedParam1: HWND, unnamedParam2: LPCWSTR, unnamedParam3: HANDLE, unnamedParam4: ULONG_PTR) -> BOOL
RT_CURSOR :: LPWSTR(uintptr(0x00000001))
RT_BITMAP :: LPWSTR(uintptr(0x00000002))
RT_ICON :: LPWSTR(uintptr(0x00000003))
RT_MENU :: LPWSTR(uintptr(0x00000004))
RT_DIALOG :: LPWSTR(uintptr(0x00000005))
RT_STRING :: LPWSTR(uintptr(0x00000006))
RT_FONTDIR :: LPWSTR(uintptr(0x00000007))
RT_FONT :: LPWSTR(uintptr(0x00000008))
RT_ACCELERATOR :: LPWSTR(uintptr(0x00000009))
RT_RCDATA :: LPWSTR(uintptr(0x0000000A))
RT_MESSAGETABLE :: LPWSTR(uintptr(0x0000000B))
RT_GROUP_CURSOR :: LPWSTR(uintptr(0x0000000C))
RT_GROUP_ICON :: LPWSTR(uintptr(0x0000000E))
RT_VERSION :: LPWSTR(uintptr(0x00000010))
RT_DLGINCLUDE :: LPWSTR(uintptr(0x00000011))
RT_PLUGPLAY :: LPWSTR(uintptr(0x00000013))
RT_VXD :: LPWSTR(uintptr(0x00000014))
RT_ANICURSOR :: LPWSTR(uintptr(0x00000015))
RT_ANIICON :: LPWSTR(uintptr(0x00000016))
RT_MANIFEST :: LPWSTR(uintptr(0x00000018))
CREATEPROCESS_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000001))
ISOLATIONAWARE_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000002))
ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000003))
ISOLATIONPOLICY_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000004))
ISOLATIONPOLICY_BROWSER_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000005))
MINIMUM_RESERVED_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000001))
MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID :: LPWSTR(uintptr(0x00000010))
ACCEL :: struct {
/* Also called the flags field */
fVirt: BYTE,
key: WORD,
cmd: WORD,
}
LPACCEL :: ^ACCEL
MIIM_STATE :: 0x00000001
MIIM_ID :: 0x00000002
MIIM_SUBMENU :: 0x00000004
MIIM_CHECKMARKS :: 0x00000008
MIIM_TYPE :: 0x00000010
MIIM_DATA :: 0x00000020
MIIM_STRING :: 0x00000040
MIIM_BITMAP :: 0x00000080
MIIM_FTYPE :: 0x00000100
MENUITEMINFOW :: struct {
cbSize: UINT,
fMask: UINT,
fType: UINT, // used if MIIM_TYPE (4.0) or MIIM_FTYPE (>4.0)
fState: UINT, // used if MIIM_STATE
wID: UINT, // used if MIIM_ID
hSubMenu: HMENU, // used if MIIM_SUBMENU
hbmpChecked: HBITMAP, // used if MIIM_CHECKMARKS
hbmpUnchecked: HBITMAP, // used if MIIM_CHECKMARKS
dwItemData: ULONG_PTR, // used if MIIM_DATA
dwTypeData: LPWSTR, // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0)
cch: UINT, // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0)
hbmpItem: HBITMAP, // used if MIIM_BITMAP
}
LPMENUITEMINFOW :: ^MENUITEMINFOW
DISPLAY_DEVICEW :: struct {
cb: DWORD,
DeviceName: [32]WCHAR,
DeviceString: [128]WCHAR,
StateFlags: DWORD,
DeviceID: [128]WCHAR,
DeviceKey: [128]WCHAR,
}
PDISPLAY_DEVICEW :: ^DISPLAY_DEVICEW
// OUTOFCONTEXT is the zero value, use {} // OUTOFCONTEXT is the zero value, use {}
WinEventFlags :: bit_set[WinEventFlag; DWORD] WinEventFlags :: bit_set[WinEventFlag; DWORD]
+59 -2
View File
@@ -6,22 +6,79 @@ import "base:intrinsics"
L :: intrinsics.constant_utf16_cstring L :: intrinsics.constant_utf16_cstring
LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD { // https://learn.microsoft.com/en-us/windows/win32/winmsg/makeword
MAKEWORD :: #force_inline proc "contextless" (#any_int a, b: int) -> WORD {
return WORD(BYTE(DWORD_PTR(a) & 0xff)) | (WORD(BYTE(DWORD_PTR(b) & 0xff)) << 8)
}
// https://learn.microsoft.com/en-us/windows/win32/winmsg/makelong
MAKELONG :: #force_inline proc "contextless" (#any_int a, b: int) -> LONG {
return LONG(WORD(DWORD_PTR(a) & 0xffff)) | (LONG(WORD(DWORD_PTR(b) & 0xffff)) << 16)
}
// https://learn.microsoft.com/en-us/windows/win32/winmsg/loword
LOWORD :: #force_inline proc "contextless" (#any_int x: int) -> WORD {
return WORD(x & 0xffff) return WORD(x & 0xffff)
} }
HIWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD { // https://learn.microsoft.com/en-us/windows/win32/winmsg/hiword
HIWORD :: #force_inline proc "contextless" (#any_int x: int) -> WORD {
return WORD(x >> 16) return WORD(x >> 16)
} }
// https://learn.microsoft.com/en-us/windows/win32/winmsg/lobyte
LOBYTE :: #force_inline proc "contextless" (w: WORD) -> BYTE {
return BYTE((DWORD_PTR(w)) & 0xff)
}
// https://learn.microsoft.com/en-us/windows/win32/winmsg/hibyte
HIBYTE :: #force_inline proc "contextless" (w: WORD) -> BYTE {
return BYTE(((DWORD_PTR(w)) >> 8) & 0xff)
}
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-makewparam
MAKEWPARAM :: #force_inline proc "contextless" (#any_int l, h: int) -> WPARAM {
return WPARAM(MAKELONG(l, h))
}
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-makelparam
MAKELPARAM :: #force_inline proc "contextless" (#any_int l, h: int) -> LPARAM {
return LPARAM(MAKELONG(l, h))
}
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-makelresult
MAKELRESULT :: #force_inline proc "contextless" (#any_int l, h: int) -> LRESULT {
return LRESULT(MAKELONG(l, h))
}
// https://learn.microsoft.com/en-us/windows/win32/api/windowsx/nf-windowsx-get_x_lparam
GET_X_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int { GET_X_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int {
return cast(c_int)cast(c_short)LOWORD(cast(DWORD)lp) return cast(c_int)cast(c_short)LOWORD(cast(DWORD)lp)
} }
// https://learn.microsoft.com/en-us/windows/win32/api/windowsx/nf-windowsx-get_y_lparam
GET_Y_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int { GET_Y_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int {
return cast(c_int)cast(c_short)HIWORD(cast(DWORD)lp) return cast(c_int)cast(c_short)HIWORD(cast(DWORD)lp)
} }
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-makelcid
MAKELCID :: #force_inline proc "contextless" (lgid, srtid: WORD) -> LCID {
return (DWORD(WORD(srtid)) << 16) | DWORD(WORD(lgid))
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-makelangid
MAKELANGID :: #force_inline proc "contextless" (p, s: WORD) -> DWORD {
return DWORD(WORD(s)) << 10 | DWORD(WORD(p))
}
LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID {
return LANGID(lcid)
}
// this one gave me trouble as it do not mask the values.
// the _ in the name is also off comparing to the c code
// i can't find any usage in the odin repo
@(deprecated = "use MAKEWORD")
MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD { MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD {
return x << 8 | y return x << 8 | y
} }
+227 -3
View File
@@ -1,6 +1,189 @@
// +build windows // +build windows
package sys_windows package sys_windows
// https://learn.microsoft.com/en-us/windows/win32/api/winerror/
// Values are 32 bit values laid out as follows:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---+-+-+-----------------------+-------------------------------+
// |Sev|C|R| Facility | Code |
// +---+-+-+-----------------------+-------------------------------+
//
// where
//
// Sev - is the severity code
//
// 00 - Success
// 01 - Informational
// 10 - Warning
// 11 - Error
//
// C - is the Customer code flag
//
// R - is a reserved bit
//
// Facility - is the facility code
//
// Code - is the facility's status code
// Define the facility codes
FACILITY :: enum DWORD {
NULL = 0,
RPC = 1,
DISPATCH = 2,
STORAGE = 3,
ITF = 4,
WIN32 = 7,
WINDOWS = 8,
SSPI = 9,
SECURITY = 9,
CONTROL = 10,
CERT = 11,
INTERNET = 12,
MEDIASERVER = 13,
MSMQ = 14,
SETUPAPI = 15,
SCARD = 16,
COMPLUS = 17,
AAF = 18,
URT = 19,
ACS = 20,
DPLAY = 21,
UMI = 22,
SXS = 23,
WINDOWS_CE = 24,
HTTP = 25,
USERMODE_COMMONLOG = 26,
WER = 27,
USERMODE_FILTER_MANAGER = 31,
BACKGROUNDCOPY = 32,
CONFIGURATION = 33,
WIA = 33,
STATE_MANAGEMENT = 34,
METADIRECTORY = 35,
WINDOWSUPDATE = 36,
DIRECTORYSERVICE = 37,
GRAPHICS = 38,
SHELL = 39,
NAP = 39,
TPM_SERVICES = 40,
TPM_SOFTWARE = 41,
UI = 42,
XAML = 43,
ACTION_QUEUE = 44,
PLA = 48,
WINDOWS_SETUP = 48,
FVE = 49,
FWP = 50,
WINRM = 51,
NDIS = 52,
USERMODE_HYPERVISOR = 53,
CMI = 54,
USERMODE_VIRTUALIZATION = 55,
USERMODE_VOLMGR = 56,
BCD = 57,
USERMODE_VHD = 58,
USERMODE_HNS = 59,
SDIAG = 60,
WEBSERVICES = 61,
WINPE = 61,
WPN = 62,
WINDOWS_STORE = 63,
INPUT = 64,
QUIC = 65,
EAP = 66,
IORING = 70,
WINDOWS_DEFENDER = 80,
OPC = 81,
XPS = 82,
MBN = 84,
POWERSHELL = 84,
RAS = 83,
P2P_INT = 98,
P2P = 99,
DAF = 100,
BLUETOOTH_ATT = 101,
AUDIO = 102,
STATEREPOSITORY = 103,
VISUALCPP = 109,
SCRIPT = 112,
PARSE = 113,
BLB = 120,
BLB_CLI = 121,
WSBAPP = 122,
BLBUI = 128,
USN = 129,
USERMODE_VOLSNAP = 130,
TIERING = 131,
WSB_ONLINE = 133,
ONLINE_ID = 134,
DEVICE_UPDATE_AGENT = 135,
DRVSERVICING = 136,
DLS = 153,
DELIVERY_OPTIMIZATION = 208,
USERMODE_SPACES = 231,
USER_MODE_SECURITY_CORE = 232,
USERMODE_LICENSING = 234,
SOS = 160,
OCP_UPDATE_AGENT = 173,
DEBUGGERS = 176,
SPP = 256,
RESTORE = 256,
DMSERVER = 256,
DEPLOYMENT_SERVICES_SERVER = 257,
DEPLOYMENT_SERVICES_IMAGING = 258,
DEPLOYMENT_SERVICES_MANAGEMENT = 259,
DEPLOYMENT_SERVICES_UTIL = 260,
DEPLOYMENT_SERVICES_BINLSVC = 261,
DEPLOYMENT_SERVICES_PXE = 263,
DEPLOYMENT_SERVICES_TFTP = 264,
DEPLOYMENT_SERVICES_TRANSPORT_MANAGEMENT = 272,
DEPLOYMENT_SERVICES_DRIVER_PROVISIONING = 278,
DEPLOYMENT_SERVICES_MULTICAST_SERVER = 289,
DEPLOYMENT_SERVICES_MULTICAST_CLIENT = 290,
DEPLOYMENT_SERVICES_CONTENT_PROVIDER = 293,
HSP_SERVICES = 296,
HSP_SOFTWARE = 297,
LINGUISTIC_SERVICES = 305,
AUDIOSTREAMING = 1094,
TTD = 1490,
ACCELERATOR = 1536,
WMAAECMA = 1996,
DIRECTMUSIC = 2168,
DIRECT3D10 = 2169,
DXGI = 2170,
DXGI_DDI = 2171,
DIRECT3D11 = 2172,
DIRECT3D11_DEBUG = 2173,
DIRECT3D12 = 2174,
DIRECT3D12_DEBUG = 2175,
DXCORE = 2176,
PRESENTATION = 2177,
LEAP = 2184,
AUDCLNT = 2185,
WINCODEC_DWRITE_DWM = 2200,
WINML = 2192,
DIRECT2D = 2201,
DEFRAG = 2304,
USERMODE_SDBUS = 2305,
JSCRIPT = 2306,
PIDGENX = 2561,
EAS = 85,
WEB = 885,
WEB_SOCKET = 886,
MOBILE = 1793,
SQLITE = 1967,
SERVICE_FABRIC = 1968,
UTC = 1989,
WEP = 2049,
SYNCENGINE = 2050,
XBOX = 2339,
GAME = 2340,
PIX = 2748,
}
ERROR_SUCCESS : DWORD : 0 ERROR_SUCCESS : DWORD : 0
NO_ERROR :: 0 NO_ERROR :: 0
SEC_E_OK : HRESULT : 0x00000000 SEC_E_OK : HRESULT : 0x00000000
@@ -42,14 +225,55 @@ ERROR_TIMEOUT : DWORD : 1460
ERROR_DATATYPE_MISMATCH : DWORD : 1629 ERROR_DATATYPE_MISMATCH : DWORD : 1629
ERROR_UNSUPPORTED_TYPE : DWORD : 1630 ERROR_UNSUPPORTED_TYPE : DWORD : 1630
ERROR_NOT_SAME_OBJECT : DWORD : 1656 ERROR_NOT_SAME_OBJECT : DWORD : 1656
ERROR_PIPE_CONNECTED : DWORD : 0x80070217 ERROR_PIPE_CONNECTED : DWORD : 535
ERROR_PIPE_BUSY : DWORD : 231 ERROR_PIPE_BUSY : DWORD : 231
E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001 // https://learn.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
S_OK :: 0x00000000 // Operation successful
E_NOTIMPL :: 0x80004001 // Not implemented
E_NOINTERFACE :: 0x80004002 // No such interface supported
E_POINTER :: 0x80004003 // Pointer that is not valid
E_ABORT :: 0x80004004 // Operation aborted
E_FAIL :: 0x80004005 // Unspecified failure
E_UNEXPECTED :: 0x8000FFFF // Unexpected failure
E_ACCESSDENIED :: 0x80070005 // General access denied error
E_HANDLE :: 0x80070006 // Handle that is not valid
E_OUTOFMEMORY :: 0x8007000E // Failed to allocate necessary memory
E_INVALIDARG :: 0x80070057 // One or more arguments are not valid
SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= 0 } // Severity values
SEVERITY :: enum DWORD {
SUCCESS = 0,
ERROR = 1,
}
// Generic test for success on any status value (non-negative numbers indicate success).
SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= S_OK }
// and the inverse
FAILED :: #force_inline proc(#any_int result: int) -> bool { return result < S_OK }
// Generic test for error on any status value.
IS_ERROR :: #force_inline proc(#any_int status: int) -> bool { return u32(status) >> 31 == u32(SEVERITY.ERROR) }
// Return the code
HRESULT_CODE :: #force_inline proc(#any_int hr: int) -> int { return int(u32(hr) & 0xFFFF) }
// Return the facility
HRESULT_FACILITY :: #force_inline proc(#any_int hr: int) -> FACILITY { return FACILITY((u32(hr) >> 16) & 0x1FFF) }
// Return the severity
HRESULT_SEVERITY :: #force_inline proc(#any_int hr: int) -> SEVERITY { return SEVERITY((u32(hr) >> 31) & 0x1) }
// Create an HRESULT value from component pieces
MAKE_HRESULT :: #force_inline proc(#any_int sev: int, #any_int fac: int, #any_int code: int) -> HRESULT {
return HRESULT((uint(sev)<<31) | (uint(fac)<<16) | (uint(code)))
}
DECODE_HRESULT :: #force_inline proc(#any_int hr: int) -> (SEVERITY, FACILITY, int) {
return HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr)
}
// aka ERROR or WIN32_ERROR to hint the WIN32 facility
System_Error :: enum DWORD { System_Error :: enum DWORD {
// The operation completed successfully. // The operation completed successfully.
SUCCESS = 0x0, SUCCESS = 0x0,
+16 -1
View File
@@ -270,7 +270,7 @@ LPHWAVEOUT :: ^HWAVEOUT
// https://learn.microsoft.com/en-us/windows/win32/multimedia/multimedia-timer-structures // https://learn.microsoft.com/en-us/windows/win32/multimedia/multimedia-timer-structures
MMTIME :: struct { MMTIME :: struct {
wType: UINT, wType: MMTIME_TYPE,
u: struct #raw_union { u: struct #raw_union {
ms: DWORD, ms: DWORD,
sample: DWORD, sample: DWORD,
@@ -292,6 +292,21 @@ MMTIME :: struct {
} }
LPMMTIME :: ^MMTIME LPMMTIME :: ^MMTIME
MMTIME_TYPE :: enum UINT {
/* time in milliseconds */
TIME_MS = 0x0001,
/* number of wave samples */
TIME_SAMPLES = 0x0002,
/* current byte offset */
TIME_BYTES = 0x0004,
/* SMPTE time */
TIME_SMPTE = 0x0008,
/* MIDI time */
TIME_MIDI = 0x0010,
/* Ticks within MIDI stream */
TIME_TICKS = 0x0020,
}
MAXPNAMELEN :: 32 MAXPNAMELEN :: 32
MAXERRORLENGTH :: 256 MAXERRORLENGTH :: 256
MMVERSION :: UINT MMVERSION :: UINT
+31
View File
@@ -0,0 +1,31 @@
// +build windows
package sys_windows
LCTYPE :: distinct DWORD
LOCALE_NAME_MAX_LENGTH :: 85
LOCALE_NAME_USER_DEFAULT :: 0
LOCALE_NAME_INVARIANT : wstring = L("")
LOCALE_NAME_SYSTEM_DEFAULT : wstring = L("!x-sys-default-locale")
// String Length Maximums.
// 5 ranges, 2 bytes ea., 0 term.
MAX_LEADBYTES :: 12
// single or double byte
MAX_DEFAULTCHAR :: 2
CPINFOEXW :: struct{
// Maximum length, in bytes, of a character in the code page.
MaxCharSize: UINT,
// The default is usually the "?" character for the code page.
DefaultChar: [MAX_DEFAULTCHAR]BYTE,
// A fixed-length array of lead byte ranges, for which the number of lead byte ranges is variable.
LeadByte: [MAX_LEADBYTES]BYTE,
// The default is usually the "?" character or the katakana middle dot character.
UnicodeDefaultChar: WCHAR,
// Code page value. This value reflects the code page passed to the GetCPInfoEx function.
CodePage: CODEPAGE,
// Full name of the code page.
CodePageName: [MAX_PATH]WCHAR,
}
LPCPINFOEXW :: ^CPINFOEXW
+92
View File
@@ -0,0 +1,92 @@
// +build windows
package sys_windows
foreign import version "system:version.lib"
@(default_calling_convention = "system")
foreign version {
GetFileVersionInfoSizeW :: proc(lpwstrFilename: LPCWSTR, lpdwHandle: LPDWORD) -> DWORD ---
GetFileVersionInfoW :: proc(lptstrFilename: LPCWSTR, dwHandle: DWORD, dwLen: DWORD, lpData: LPVOID) -> BOOL ---
GetFileVersionInfoSizeExW :: proc(dwFlags: FILE_VER_GET_FLAGS, lpwstrFilename: LPCWSTR, lpdwHandle: LPDWORD) -> DWORD ---
GetFileVersionInfoExW :: proc(dwFlags: FILE_VER_GET_FLAGS, lpwstrFilename: LPCWSTR, dwHandle, dwLen: DWORD, lpData: LPVOID) -> DWORD ---
VerLanguageNameW :: proc(wLang: DWORD, szLang: LPWSTR, cchLang: DWORD) -> DWORD ---
VerQueryValueW :: proc(pBlock: LPCVOID, lpSubBlock: LPCWSTR, lplpBuffer: ^LPVOID, puLen: PUINT) -> BOOL ---
}
FILE_VER_GET :: enum DWORD {LOCALISED, NEUTRAL, PREFETCHED}
FILE_VER_GET_FLAGS :: bit_set[FILE_VER_GET; DWORD]
/* ----- Symbols ----- */
VS_FILE_INFO :: RT_VERSION
VS_VERSION_INFO :: 1
VS_USER_DEFINED :: 100
VS_FFI_SIGNATURE : DWORD : 0xFEEF04BD
VS_FFI_STRUCVERSION :: 0x00010000
VS_FFI_FILEFLAGSMASK :: 0x0000003F
/* ----- VS_VERSION.dwFileFlags ----- */
VS_FILEFLAG :: enum DWORD {
DEBUG,
PRERELEASE,
PATCHED,
PRIVATEBUILD,
INFOINFERRED,
SPECIALBUILD,
}
VS_FILEFLAGS :: bit_set[VS_FILEFLAG;DWORD]
/* ----- VS_VERSION.dwFileOS ----- */
VOS :: enum WORD {
UNKNOWN = 0x0000,
DOS = 0x0001,
OS216 = 0x0002,
OS232 = 0x0003,
NT = 0x0004,
WINCE = 0x0005,
}
VOS2 :: enum WORD {
BASE = 0x0000,
WINDOWS16 = 0x0001,
PM16 = 0x0002,
PM32 = 0x0003,
WINDOWS32 = 0x0004,
}
/* ----- VS_VERSION.dwFileType ----- */
VFT :: enum DWORD {
UNKNOWN = 0x00000000,
APP = 0x00000001,
DLL = 0x00000002,
DRV = 0x00000003,
FONT = 0x00000004,
VXD = 0x00000005,
STATIC_LIB = 0x00000007,
}
/* ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_DRV ----- */
VFT2_WINDOWS_DRV :: enum DWORD {
UNKNOWN = 0x00000000,
DRV_PRINTER = 0x00000001,
DRV_KEYBOARD = 0x00000002,
DRV_LANGUAGE = 0x00000003,
DRV_DISPLAY = 0x00000004,
DRV_MOUSE = 0x00000005,
DRV_NETWORK = 0x00000006,
DRV_SYSTEM = 0x00000007,
DRV_INSTALLABLE = 0x00000008,
DRV_SOUND = 0x00000009,
DRV_COMM = 0x0000000A,
DRV_INPUTMETHOD = 0x0000000B,
DRV_VERSIONED_PRINTER = 0x0000000C,
}
/* ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_FONT ----- */
VFT2_WINDOWS_FONT :: enum DWORD {
FONT_RASTER = 0x00000001,
FONT_VECTOR = 0x00000002,
FONT_TRUETYPE = 0x00000003,
}
+2 -2
View File
@@ -868,8 +868,8 @@ To partly mitigate this, redirect STDERR to a file or use the -define:ODIN_TEST_
when ODIN_OS != .Windows { when ODIN_OS != .Windows {
mode = os.S_IRUSR|os.S_IWUSR|os.S_IRGRP|os.S_IROTH mode = os.S_IRUSR|os.S_IWUSR|os.S_IRGRP|os.S_IROTH
} }
json_fd, errno := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) json_fd, err := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
fmt.assertf(errno == os.ERROR_NONE, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, errno) fmt.assertf(err == nil, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, err)
defer os.close(json_fd) defer os.close(json_fd)
for test, i in report.all_tests { for test, i in report.all_tests {
+1 -1
View File
@@ -137,7 +137,7 @@ clear_all :: proc(s: ^State) -> (cleared: bool) {
// push current text state to the wanted undo|redo stack // push current text state to the wanted undo|redo stack
undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) -> mem.Allocator_Error { undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) -> mem.Allocator_Error {
if s.builder != nil { if s.builder == nil {
return nil return nil
} }
text := string(s.builder.buf[:]) text := string(s.builder.buf[:])
+321 -32
View File
@@ -232,27 +232,21 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
} }
/* /*
Round a duration to a specific unit. Round a duration to a specific unit
This procedure rounds the duration to a specific unit. This procedure rounds the duration to a specific unit
**Inputs**:
- `d`: The duration to round.
- `m`: The unit to round to.
**Returns**:
- The duration `d`, rounded to the unit specified by `m`.
**Example**:
In order to obtain the rough amount of seconds in a duration, the following call
can be used:
```
time.duration_round(my_duration, time.Second)
```
**Note**: Any duration can be supplied as a unit. **Note**: Any duration can be supplied as a unit.
Inputs:
- d: The duration to round
- m: The unit to round to
Returns:
- The duration `d`, rounded to the unit specified by `m`
Example:
time.duration_round(my_duration, time.Second)
*/ */
duration_round :: proc "contextless" (d, m: Duration) -> Duration { duration_round :: proc "contextless" (d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool { _less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
@@ -288,23 +282,17 @@ Truncate the duration to the specified unit.
This procedure truncates the duration `d` to the unit specified by `m`. This procedure truncates the duration `d` to the unit specified by `m`.
**Inputs**: **Note**: Any duration can be supplied as a unit.
- `d`: The duration to truncate.
- `m`: The unit to truncate to.
**Returns**: Inputs:
- d: The duration to truncate.
- m: The unit to truncate to.
Returns:
- The duration `d`, truncated to the unit specified by `m`. - The duration `d`, truncated to the unit specified by `m`.
**Example**: Example:
time.duration_round(my_duration, time.Second)
In order to obtain the amount of whole seconds in a duration, the following call
can be used:
```
time.duration_round(my_duration, time.Second)
```
**Note**: Any duration can be supplied as a unit.
*/ */
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration { duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
return d if m <= 0 else d - d%m return d if m <= 0 else d - d%m
@@ -389,6 +377,307 @@ clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
return return
} }
MIN_HMS_LEN :: 8
MIN_HMS_12_LEN :: 11
MIN_YYYY_DATE_LEN :: 10
MIN_YY_DATE_LEN :: 8
/*
Formats a `Time` as a 24-hour `hh:mm:ss` string.
**Does not allocate**
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by buf
Example:
buf: [MIN_HMS_LEN]u8
now := time.now()
fmt.println(time.to_string_hms(now, buf[:]))
*/
time_to_string_hms :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_HMS_LEN)
h, m, s := clock(t)
buf[7] = '0' + u8(s % 10); s /= 10
buf[6] = '0' + u8(s)
buf[5] = ':'
buf[4] = '0' + u8(m % 10); m /= 10
buf[3] = '0' + u8(m)
buf[2] = ':'
buf[1] = '0' + u8(h % 10); h /= 10
buf[0] = '0' + u8(h)
return string(buf[:MIN_HMS_LEN])
}
/*
Formats a `Duration` as a 24-hour `hh:mm:ss` string.
**Does not allocate**
Inputs:
- d: The Duration to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by buf
Example:
buf: [MIN_HMS_LEN]u8
d := time.since(earlier)
fmt.println(time.to_string_hms(now, buf[:]))
*/
duration_to_string_hms :: proc(d: Duration, buf: []u8) -> (res: string) #no_bounds_check {
return time_to_string_hms(Time{_nsec=i64(d)}, buf)
}
to_string_hms :: proc{time_to_string_hms, duration_to_string_hms}
/*
Formats a `Time` as a 12-hour `hh:mm:ss pm` string
**Does not allocate**
Inputs:
- t: The Time to format
- buf: The backing buffer to use
- ampm: An optional pair of am/pm strings to use in place of the default
Returns:
- res: The formatted string, backed by buf
Example:
buf: [64]u8
now := time.now()
fmt.println(time.to_string_hms_12(now, buf[:]))
fmt.println(time.to_string_hms_12(now, buf[:], {"㏂", "㏘"}))
*/
to_string_hms_12 :: proc(t: Time, buf: []u8, ampm: [2]string = {" am", " pm"}) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_HMS_LEN + max(len(ampm[0]), len(ampm[1])))
h, m, s := clock(t)
_h := h % 12
buf[7] = '0' + u8(s % 10); s /= 10
buf[6] = '0' + u8(s)
buf[5] = ':'
buf[4] = '0' + u8(m % 10); m /= 10
buf[3] = '0' + u8(m)
buf[2] = ':'
buf[1] = '0' + u8(_h% 10); _h /= 10
buf[0] = '0' + u8(_h)
if h < 13 {
copy(buf[8:], ampm[0])
return string(buf[:MIN_HMS_LEN+len(ampm[0])])
} else {
copy(buf[8:], ampm[1])
return string(buf[:MIN_HMS_LEN+len(ampm[1])])
}
}
/*
Formats a Time as a yyyy-mm-dd date string.
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by `buf`.
Example:
buf: [MIN_YYYY_DATE_LEN]u8
now := time.now()
fmt.println(time.to_string_yyyy_mm_dd(now, buf[:]))
*/
to_string_yyyy_mm_dd :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_YYYY_DATE_LEN)
y, _m, d := date(t)
m := u8(_m)
buf[9] = '0' + u8(d % 10); d /= 10
buf[8] = '0' + u8(d % 10)
buf[7] = '-'
buf[6] = '0' + u8(m % 10); m /= 10
buf[5] = '0' + u8(m % 10)
buf[4] = '-'
buf[3] = '0' + u8(y % 10); y /= 10
buf[2] = '0' + u8(y % 10); y /= 10
buf[1] = '0' + u8(y % 10); y /= 10
buf[0] = '0' + u8(y)
return string(buf[:MIN_YYYY_DATE_LEN])
}
/*
Formats a Time as a yy-mm-dd date string.
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by `buf`.
Example:
buf: [MIN_YY_DATE_LEN]u8
now := time.now()
fmt.println(time.to_string_yy_mm_dd(now, buf[:]))
*/
to_string_yy_mm_dd :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_YY_DATE_LEN)
y, _m, d := date(t)
y %= 100; m := u8(_m)
buf[7] = '0' + u8(d % 10); d /= 10
buf[6] = '0' + u8(d % 10)
buf[5] = '-'
buf[4] = '0' + u8(m % 10); m /= 10
buf[3] = '0' + u8(m % 10)
buf[2] = '-'
buf[1] = '0' + u8(y % 10); y /= 10
buf[0] = '0' + u8(y)
return string(buf[:MIN_YY_DATE_LEN])
}
/*
Formats a Time as a dd-mm-yyyy date string.
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by `buf`.
Example:
buf: [MIN_YYYY_DATE_LEN]u8
now := time.now()
fmt.println(time.to_string_dd_mm_yyyy(now, buf[:]))
*/
to_string_dd_mm_yyyy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_YYYY_DATE_LEN)
y, _m, d := date(t)
m := u8(_m)
buf[9] = '0' + u8(y % 10); y /= 10
buf[8] = '0' + u8(y % 10); y /= 10
buf[7] = '0' + u8(y % 10); y /= 10
buf[6] = '0' + u8(y)
buf[5] = '-'
buf[4] = '0' + u8(m % 10); m /= 10
buf[3] = '0' + u8(m % 10)
buf[2] = '-'
buf[1] = '0' + u8(d % 10); d /= 10
buf[0] = '0' + u8(d % 10)
return string(buf[:MIN_YYYY_DATE_LEN])
}
/*
Formats a Time as a dd-mm-yy date string.
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by `buf`.
Example:
buf: [MIN_YY_DATE_LEN]u8
now := time.now()
fmt.println(time.to_string_dd_mm_yy(now, buf[:]))
*/
to_string_dd_mm_yy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_YY_DATE_LEN)
y, _m, d := date(t)
y %= 100; m := u8(_m)
buf[7] = '0' + u8(y % 10); y /= 10
buf[6] = '0' + u8(y)
buf[5] = '-'
buf[4] = '0' + u8(m % 10); m /= 10
buf[3] = '0' + u8(m % 10)
buf[2] = '-'
buf[1] = '0' + u8(d % 10); d /= 10
buf[0] = '0' + u8(d % 10)
return string(buf[:MIN_YY_DATE_LEN])
}
/*
Formats a Time as a mm-dd-yyyy date string.
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by `buf`.
Example:
buf: [MIN_YYYY_DATE_LEN]u8
now := time.now()
fmt.println(time.to_string_mm_dd_yyyy(now, buf[:]))
*/
to_string_mm_dd_yyyy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_YYYY_DATE_LEN)
y, _m, d := date(t)
m := u8(_m)
buf[9] = '0' + u8(y % 10); y /= 10
buf[8] = '0' + u8(y % 10); y /= 10
buf[7] = '0' + u8(y % 10); y /= 10
buf[6] = '0' + u8(y)
buf[5] = '-'
buf[4] = '0' + u8(d % 10); d /= 10
buf[3] = '0' + u8(d % 10)
buf[2] = '-'
buf[1] = '0' + u8(m % 10); m /= 10
buf[0] = '0' + u8(m % 10)
return string(buf[:MIN_YYYY_DATE_LEN])
}
/*
Formats a Time as a mm-dd-yy date string.
Inputs:
- t: The Time to format.
- buf: The backing buffer to use.
Returns:
- res: The formatted string, backed by `buf`.
Example:
buf: [MIN_YY_DATE_LEN]u8
now := time.now()
fmt.println(time.to_string_mm_dd_yy(now, buf[:]))
*/
to_string_mm_dd_yy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
assert(len(buf) >= MIN_YY_DATE_LEN)
y, _m, d := date(t)
y %= 100; m := u8(_m)
buf[7] = '0' + u8(y % 10); y /= 10
buf[6] = '0' + u8(y)
buf[5] = '-'
buf[4] = '0' + u8(d % 10); d /= 10
buf[3] = '0' + u8(d % 10)
buf[2] = '-'
buf[1] = '0' + u8(m % 10); m /= 10
buf[0] = '0' + u8(m % 10)
return string(buf[:MIN_YY_DATE_LEN])
}
/* /*
Read the timestamp counter of the CPU. Read the timestamp counter of the CPU.
*/ */
+1 -1
View File
@@ -359,7 +359,7 @@ control_flow :: proc() {
if false { if false {
f, err := os.open("my_file.txt") f, err := os.open("my_file.txt")
if err != os.ERROR_NONE { if err != nil {
// handle error // handle error
} }
defer os.close(f) defer os.close(f)
+11 -3
View File
@@ -1822,6 +1822,7 @@ gb_internal bool init_build_paths(String init_filename) {
if (bc->resource_filepath.len > 0) { if (bc->resource_filepath.len > 0) {
bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath); bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
if (!string_ends_with(bc->resource_filepath, str_lit(".res"))) { if (!string_ends_with(bc->resource_filepath, str_lit(".res"))) {
bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath); bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc")); bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
} }
@@ -2010,15 +2011,22 @@ gb_internal bool init_build_paths(String init_filename) {
} }
} }
// Check if output path is a directory.
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text)); defer (gb_free(ha, output_file.text));
// Check if output path is a directory.
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
return false; return false;
} }
if (!write_directory(bc->build_paths[BuildPath_Output].basename)) { gbFile output_file_test;
const char* output_file_name = (const char*)output_file.text;
gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name);
gb_file_close(&output_file_test);
gb_file_remove(output_file_name);
if (output_test_err != 0) {
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text)); defer (gb_free(ha, output_file.text));
gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file)); gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
+115 -7
View File
@@ -470,8 +470,8 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
} }
// Integer only // Integer only
case BuiltinProc_simd_add_sat: case BuiltinProc_simd_saturating_add:
case BuiltinProc_simd_sub_sat: case BuiltinProc_simd_saturating_sub:
case BuiltinProc_simd_bit_and: case BuiltinProc_simd_bit_and:
case BuiltinProc_simd_bit_or: case BuiltinProc_simd_bit_or:
case BuiltinProc_simd_bit_xor: case BuiltinProc_simd_bit_xor:
@@ -501,8 +501,8 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
Type *elem = base_array_type(x.type); Type *elem = base_array_type(x.type);
switch (id) { switch (id) {
case BuiltinProc_simd_add_sat: case BuiltinProc_simd_saturating_add:
case BuiltinProc_simd_sub_sat: case BuiltinProc_simd_saturating_sub:
if (!is_type_integer(elem)) { if (!is_type_integer(elem)) {
gbString xs = type_to_string(x.type); gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs);
@@ -663,6 +663,91 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true; return true;
} }
case BuiltinProc_simd_gather:
case BuiltinProc_simd_scatter:
case BuiltinProc_simd_masked_load:
case BuiltinProc_simd_masked_store:
case BuiltinProc_simd_masked_expand_load:
case BuiltinProc_simd_masked_compress_store:
{
// gather (ptr: #simd[N]rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
// scatter(ptr: #simd[N]rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
// masked_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
// masked_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
// masked_expand_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
// masked_compress_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
Operand ptr = {};
Operand values = {};
Operand mask = {};
check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
check_expr(c, &values, ce->args[1]); if (values.mode == Addressing_Invalid) return false;
check_expr(c, &mask, ce->args[2]); if (mask.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(values.type)) { error(values.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; }
if (!is_type_simd_vector(mask.type)) { error(mask.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; }
if (id == BuiltinProc_simd_gather || id == BuiltinProc_simd_scatter) {
if (!is_type_simd_vector(ptr.type)) { error(ptr.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; }
Type *ptr_elem = base_array_type(ptr.type);
if (!is_type_rawptr(ptr_elem)) {
gbString s = type_to_string(ptr.type);
error(ptr.expr, "Expected a simd vector of 'rawptr' for the addresses, got %s", s);
gb_string_free(s);
return false;
}
} else {
if (!is_type_pointer(ptr.type)) {
gbString s = type_to_string(ptr.type);
error(ptr.expr, "Expected a pointer type for the address, got %s", s);
gb_string_free(s);
return false;
}
}
Type *mask_elem = base_array_type(mask.type);
if (!is_type_integer(mask_elem) && !is_type_boolean(mask_elem)) {
gbString s = type_to_string(mask.type);
error(mask.expr, "Expected a simd vector of integers or booleans for the mask, got %s", s);
gb_string_free(s);
return false;
}
if (id == BuiltinProc_simd_gather || id == BuiltinProc_simd_scatter) {
i64 ptr_count = get_array_type_count(ptr.type);
i64 values_count = get_array_type_count(values.type);
i64 mask_count = get_array_type_count(mask.type);
if (ptr_count != values_count ||
values_count != mask_count ||
mask_count != ptr_count) {
gbString s = type_to_string(mask.type);
error(mask.expr, "All simd vectors must be of the same length, got %lld vs %lld vs %lld", cast(long long)ptr_count, cast(long long)values_count, cast(long long)mask_count);
gb_string_free(s);
return false;
}
} else {
i64 values_count = get_array_type_count(values.type);
i64 mask_count = get_array_type_count(mask.type);
if (values_count != mask_count) {
gbString s = type_to_string(mask.type);
error(mask.expr, "All simd vectors must be of the same length, got %lld vs %lld", cast(long long)values_count, cast(long long)mask_count);
gb_string_free(s);
return false;
}
}
if (id == BuiltinProc_simd_gather ||
id == BuiltinProc_simd_masked_load ||
id == BuiltinProc_simd_masked_expand_load) {
operand->mode = Addressing_Value;
operand->type = values.type;
} else {
operand->mode = Addressing_NoValue;
operand->type = nullptr;
}
return true;
}
case BuiltinProc_simd_extract: case BuiltinProc_simd_extract:
{ {
Operand x = {}; Operand x = {};
@@ -775,6 +860,29 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true; return true;
} }
case BuiltinProc_simd_reduce_any:
case BuiltinProc_simd_reduce_all:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_boolean(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with a boolean element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
operand->mode = Addressing_Value;
operand->type = t_untyped_bool;
return true;
}
case BuiltinProc_simd_shuffle: case BuiltinProc_simd_shuffle:
{ {
@@ -2460,7 +2568,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
arg_count++; arg_count++;
} }
if (arg_count > max_count) { if (false && arg_count > max_count) {
error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count); error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count);
return false; return false;
} else if (arg_count < 2) { } else if (arg_count < 2) {
@@ -4302,8 +4410,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
} }
break; break;
case BuiltinProc_add_sat: case BuiltinProc_saturating_add:
case BuiltinProc_sub_sat: case BuiltinProc_saturating_sub:
{ {
Operand x = {}; Operand x = {};
Operand y = {}; Operand y = {};
+31
View File
@@ -127,6 +127,8 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y); gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
gb_internal bool is_exact_value_zero(ExactValue const &v);
enum LoadDirectiveResult { enum LoadDirectiveResult {
LoadDirective_Success = 0, LoadDirective_Success = 0,
LoadDirective_Error = 1, LoadDirective_Error = 1,
@@ -4457,6 +4459,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
case Type_Union: case Type_Union:
// IMPORTANT NOTE HACK(bill): This is just to allow for comparisons against `0` with the `os.Error` type
// as a kind of transition period
if (!build_context.strict_style &&
operand->mode == Addressing_Constant &&
target_type->kind == Type_Named &&
(c->pkg == nullptr || c->pkg->name != "os") &&
target_type->Named.name == "Error") {
Entity *e = target_type->Named.type_name;
if (e->pkg && e->pkg->name == "os") {
if (is_exact_value_zero(operand->value) &&
(operand->value.kind == ExactValue_Integer ||
operand->value.kind == ExactValue_Float)) {
operand->mode = Addressing_Value;
target_type = t_untyped_nil;
operand->value = empty_exact_value;
update_untyped_expr_value(c, operand->expr, operand->value);
break;
}
}
}
// "fallthrough"
if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) { if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
TEMPORARY_ALLOCATOR_GUARD(); TEMPORARY_ALLOCATOR_GUARD();
@@ -5135,6 +5158,14 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Scope *import_scope = e->ImportName.scope; Scope *import_scope = e->ImportName.scope;
String entity_name = selector->Ident.token.string; String entity_name = selector->Ident.token.string;
if (import_scope == nullptr) {
ERROR_BLOCK();
error(node, "'%.*s' is not imported in this file, '%.*s' is unavailable", LIT(import_name), LIT(entity_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
return nullptr;
}
check_op_expr = false; check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name); entity = scope_lookup_current(import_scope, entity_name);
bool allow_builtin = false; bool allow_builtin = false;
+1 -1
View File
@@ -3171,7 +3171,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
} else if (name == "simd") { } else if (name == "simd") {
if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
gbString str = type_to_string(elem); gbString str = type_to_string(elem);
error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); error(at->elem, "Invalid element type for #simd, expected an integer, float, boolean, or 'rawptr' with no specific endianness, got '%s'", str);
gb_string_free(str); gb_string_free(str);
*type = alloc_type_array(elem, count, generic_type); *type = alloc_type_array(elem, count, generic_type);
return; return;
+2 -2
View File
@@ -1651,9 +1651,9 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
if (mode == Addressing_Constant || mode == Addressing_Invalid) { if (mode == Addressing_Constant || mode == Addressing_Invalid) {
expr->tav.value = value; expr->tav.value = value;
} else if (mode == Addressing_Value && is_type_typeid(type)) { } else if (mode == Addressing_Value && type != nullptr && is_type_typeid(type)) {
expr->tav.value = value; expr->tav.value = value;
} else if (mode == Addressing_Value && is_type_proc(type)) { } else if (mode == Addressing_Value && type != nullptr && is_type_proc(type)) {
expr->tav.value = value; expr->tav.value = value;
} }
+28 -8
View File
@@ -70,8 +70,8 @@ enum BuiltinProcId {
BuiltinProc_overflow_sub, BuiltinProc_overflow_sub,
BuiltinProc_overflow_mul, BuiltinProc_overflow_mul,
BuiltinProc_add_sat, BuiltinProc_saturating_add,
BuiltinProc_sub_sat, BuiltinProc_saturating_sub,
BuiltinProc_sqrt, BuiltinProc_sqrt,
BuiltinProc_fused_mul_add, BuiltinProc_fused_mul_add,
@@ -141,8 +141,8 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_shl_masked, // C logic BuiltinProc_simd_shl_masked, // C logic
BuiltinProc_simd_shr_masked, // C logic BuiltinProc_simd_shr_masked, // C logic
BuiltinProc_simd_add_sat, // saturation arithmetic BuiltinProc_simd_saturating_add, // saturation arithmetic
BuiltinProc_simd_sub_sat, // saturation arithmetic BuiltinProc_simd_saturating_sub, // saturation arithmetic
BuiltinProc_simd_bit_and, BuiltinProc_simd_bit_and,
BuiltinProc_simd_bit_or, BuiltinProc_simd_bit_or,
@@ -174,6 +174,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_reduce_or, BuiltinProc_simd_reduce_or,
BuiltinProc_simd_reduce_xor, BuiltinProc_simd_reduce_xor,
BuiltinProc_simd_reduce_any,
BuiltinProc_simd_reduce_all,
BuiltinProc_simd_shuffle, BuiltinProc_simd_shuffle,
BuiltinProc_simd_select, BuiltinProc_simd_select,
@@ -188,6 +191,12 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_lanes_rotate_left, BuiltinProc_simd_lanes_rotate_left,
BuiltinProc_simd_lanes_rotate_right, BuiltinProc_simd_lanes_rotate_right,
BuiltinProc_simd_gather,
BuiltinProc_simd_scatter,
BuiltinProc_simd_masked_load,
BuiltinProc_simd_masked_store,
BuiltinProc_simd_masked_expand_load,
BuiltinProc_simd_masked_compress_store,
// Platform specific SIMD intrinsics // Platform specific SIMD intrinsics
BuiltinProc_simd_x86__MM_SHUFFLE, BuiltinProc_simd_x86__MM_SHUFFLE,
@@ -396,8 +405,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("saturating_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("saturating_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -467,8 +476,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_saturating_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_saturating_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_bit_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_bit_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_bit_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_bit_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -501,6 +510,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_all"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -515,6 +528,13 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_gather"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_scatter"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_masked_load"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_masked_store"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_masked_expand_load"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_masked_compress_store"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

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