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
copyback: false
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
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
run: |
set -e -x
git config --global --add safe.directory $(pwd)
gmake release
./odin version
@@ -88,13 +89,13 @@ jobs:
- name: Download LLVM (MacOS Intel)
if: matrix.os == 'macos-13'
run: |
brew install llvm@17
brew install llvm@17 lua@5.4
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Download LLVM (MacOS ARM)
if: matrix.os == 'macos-14'
run: |
brew install llvm@17 wasmtime
brew install llvm@17 wasmtime lua@5.4
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Build Odin
@@ -204,6 +205,7 @@ jobs:
shell: cmd
run: |
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
- name: Odin internals tests
shell: cmd
-1
View File
@@ -61,7 +61,6 @@ jobs:
mkdir dist
cp odin dist
cp LICENSE dist
cp libLLVM* dist
cp -r shared dist
cp -r base 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_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) ---
sub_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
saturating_add :: 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))) ---
@@ -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_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
// (x << y) if y <= mask else 0
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_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_or :: 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_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_mul_ordered :: proc(a: #simd[N]T) -> T ---
simd_reduce_min :: proc(a: #simd[N]T) -> T ---
simd_reduce_max :: proc(a: #simd[N]T) -> T ---
simd_reduce_and :: proc(a: #simd[N]T) -> T ---
simd_reduce_or :: proc(a: #simd[N]T) -> T ---
simd_reduce_xor :: 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 where type_is_integer(T) || type_is_float(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 where type_is_integer(T) || type_is_float(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 where type_is_integer(T) || type_is_float(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_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) ---
// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
// equivalent to a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
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_rotate_right :: 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_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
// 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,
Essence,
FreeBSD,
Haiku,
OpenBSD,
NetBSD,
Haiku,
WASI,
JS,
Orca,
Freestanding,
}
*/
@@ -589,7 +590,7 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
Memory = 1,
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
*/
+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.
//
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))
space := size + a - 1
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]
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
} else {
@@ -36,12 +39,19 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
diff := int(aligned_ptr - ptr)
if (size + diff) > space || allocated_mem == nil {
aligned_free(old_ptr)
aligned_free(allocated_mem)
return nil, .Out_Of_Memory
}
aligned_mem = rawptr(aligned_ptr)
([^]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
}
@@ -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) {
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
if zero_memory && new_size > old_size {
@@ -68,7 +78,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment, nil, mode == .Alloc)
return aligned_alloc(size, alignment, nil, 0, mode == .Alloc)
case .Free:
aligned_free(old_memory)
@@ -77,9 +87,6 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, .Mode_Not_Implemented
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)
case .Query_Features:
+1 -1
View File
@@ -95,7 +95,7 @@ Linux)
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
# Copy libLLVM*.so into current directory for linking
# 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"
;;
OpenBSD)
+3 -3
View File
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
@(private)
_rand_bytes :: proc(dst: []byte) {
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != os.ERROR_NONE {
switch ret {
ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != nil {
#partial switch ret {
case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid.
// 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
defer csv.reader_destroy(&r)
handle, errno := os.open(filename)
if errno != os.ERROR_NONE {
fmt.printfln("Error opening file: %v", filename)
handle, err := os.open(filename)
if err != nil {
fmt.eprintfln("Error opening file: %v", filename)
return
}
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.
Open_File_Error :: struct {
filename: string,
errno: os.Errno,
errno: os.Error,
mode: 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)
if errno != 0 {
// NOTE(Feoramund): os.Errno is system-dependent, and there's
if errno != nil {
// NOTE(Feoramund): os.Error is system-dependent, and there's
// currently no good way to translate them all into strings.
//
// The upcoming `os2` package will hopefully solve this.
+65 -2
View File
@@ -112,7 +112,8 @@ Image_Option:
`.alpha_drop_if_present`
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,
unless you select `alpha_premultiply`.
@@ -587,6 +588,32 @@ Channel :: enum u8 {
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.
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
// 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))
}
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
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}
// 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.
// 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 {
f, err := os.open(path)
if err != 0 {
if err != nil {
return .Unknown
}
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)
if err != 0 {
if err != nil {
return false
}
defer close(fd)
+1 -1
View File
@@ -450,7 +450,7 @@ when false {
}
fd, fderr := open(filename, flags, mode)
if fderr != 0 {
if fderr != nil {
return .Cannot_Open_File
}
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 {
a0 := normalize0(a)
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 {
c := normalize0(conj(a) * b)
+1 -1
View File
@@ -58,7 +58,7 @@ Example:
import "core:fmt"
set_global_seed_example :: proc() {
rand.set_global_seed(1)
rand.reset(1)
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) {
fd, err := os.open(filename, os.O_RDWR)
if err != 0 {
if err != nil {
return nil, .Open_Failure
}
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) {
size, os_err := os.file_size(os.Handle(fd))
if os_err != 0 {
if os_err != nil {
return nil, .Stat_Failure
}
if size < 0 {
+40 -32
View File
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
unreachable()
}
sock, ok := os.socket(c_family, c_type, c_protocol)
if ok != os.ERROR_NONE {
err = Create_Socket_Error(ok)
sock, sock_err := os.socket(c_family, c_type, c_protocol)
if sock_err != nil {
err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
return
}
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE {
err = Dial_Error(res)
if res != nil {
err = Dial_Error(os.is_platform_error(res) or_else -1)
return
}
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt)
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 {
err = .Privileged_Port_Without_Root
} else {
err = Bind_Error(res)
err = Bind_Error(os.is_platform_error(res) or_else -1)
}
}
return
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
bind(sock, interface_endpoint) or_return
res := os.listen(os.Socket(skt), backlog)
if res != os.ERROR_NONE {
err = Listen_Error(res)
if res != nil {
err = Listen_Error(os.is_platform_error(res) or_else -1)
return
}
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
sockaddr: os.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr))
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
if ok != os.ERROR_NONE {
err = Accept_Error(ok)
client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
if client_sock_err != nil {
err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
return
}
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 {
return
}
res, ok := os.recv(os.Socket(skt), buf, 0)
if ok != os.ERROR_NONE {
err = TCP_Recv_Error(ok)
res, res_err := os.recv(os.Socket(skt), buf, 0)
if res_err != nil {
err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return
}
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
fromsize := c.int(size_of(from))
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
if ok != os.ERROR_NONE {
err = UDP_Recv_Error(ok)
res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
if res_err != nil {
err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return
}
@@ -194,9 +194,13 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
res, ok := os.send(os.Socket(skt), remaining, 0)
if ok != os.ERROR_NONE {
err = TCP_Send_Error(ok)
res, res_err := os.send(os.Socket(skt), remaining, os.MSG_NOSIGNAL)
if res_err == os.EPIPE {
// 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
}
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) {
limit := min(1<<31, len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
if ok != os.ERROR_NONE {
err = UDP_Send_Error(ok)
res, res_err := os.sendto(os.Socket(skt), remaining, os.MSG_NOSIGNAL, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
if res_err == os.EPIPE {
// 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
}
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) {
s := any_socket_to_socket(skt)
res := os.shutdown(os.Socket(s), int(manner))
if res != os.ERROR_NONE {
return Shutdown_Error(res)
if res != nil {
return Shutdown_Error(os.is_platform_error(res) or_else -1)
}
return
}
@@ -302,8 +310,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
skt := any_socket_to_socket(s)
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
if res != os.ERROR_NONE {
return Socket_Option_Error(res)
if res != nil {
return Socket_Option_Error(os.is_platform_error(res) or_else -1)
}
return nil
@@ -314,8 +322,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
socket := any_socket_to_socket(socket)
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
if getfl_err != os.ERROR_NONE {
return Set_Blocking_Error(getfl_err)
if getfl_err != nil {
return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
}
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)
if setfl_err != os.ERROR_NONE {
return Set_Blocking_Error(setfl_err)
if setfl_err != nil {
return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
}
return nil
+1 -1
View File
@@ -757,7 +757,7 @@ Array_Type :: struct {
using node: Expr,
open: tokenizer.Pos,
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,
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))
}
e := ast.new(ast.Ellipsis, type.pos, type)
e.tok = tok.kind
e.expr = type
return e
}
@@ -2844,7 +2845,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
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.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 "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) {
// Ignore "." and ".."
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)
if !dir_fi.is_dir {
return nil, ERROR_FILE_IS_NOT_DIR
return nil, .Not_Dir
}
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)
wpath: []u16
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
if len(wpath) == 0 || err != ERROR_NONE {
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
if len(wpath) == 0 {
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)
wpath_search[len(wpath)+0] = '\\'
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_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE {
err = Errno(win32.GetLastError())
err = get_last_error()
return dfi[:], err
}
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) {
e := Errno(win32.GetLastError())
e := get_last_error()
if e == ERROR_NO_MORE_FILES {
break
}
@@ -109,5 +110,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
}
}
return dfi[:], ERROR_NONE
return dfi[:], nil
}
+19 -19
View File
@@ -7,27 +7,22 @@ import "base:runtime"
// 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
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" {
return
}
wkey := win32.utf8_to_wstring(key)
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 {
err := win32.GetLastError()
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
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)))
if n == 0 {
err := win32.GetLastError()
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true
@@ -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
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
// 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)
v := win32.utf8_to_wstring(value)
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 :: proc(key: string) -> Errno {
unset_env :: proc(key: string) -> Error {
k := win32.utf8_to_wstring(key)
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"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator := context.allocator) -> []string {
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
if envs == nil {
return nil
}
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 {
if c := envs[i]; c == 0 {
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 "core:unicode/utf16"
@(require_results)
is_path_separator :: proc(c: byte) -> bool {
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 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND
return INVALID_HANDLE, General_Error.Not_Exist
}
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)
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 {
return handle, ERROR_NONE
return handle, nil
}
err := Errno(win32.GetLastError())
return INVALID_HANDLE, err
return INVALID_HANDLE, get_last_error()
}
close :: proc(fd: Handle) -> Errno {
close :: proc(fd: Handle) -> Error {
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)) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
return
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 {
return 0, ERROR_NONE
return 0, nil
}
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)
if single_write_length <= 0 || !e {
err := Errno(win32.GetLastError())
return int(total_write), err
return int(total_write), get_last_error()
}
total_write += i64(single_write_length)
}
return int(total_write), ERROR_NONE
return int(total_write), nil
}
@(private="file")
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
@(private="file", require_results)
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 {
return 0, 0
return 0, nil
}
BUF_SIZE :: 386
buf16: [BUF_SIZE]u16
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)
max_read := u32(min(BUF_SIZE, min_read))
if max_read == 0 {
@@ -118,7 +118,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
single_read_length: u32
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
if !ok {
err = Errno(win32.GetLastError())
err = get_last_error()
}
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
}
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 {
return 0, ERROR_NONE
return 0, nil
}
handle := win32.HANDLE(fd)
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
if is_console {
total_read, err = read_console(handle, data[total_read:][:to_read])
if err != 0 {
if err != nil {
return total_read, err
}
} 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:
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
if bytes_read == 0 {
return 0, ERROR_HANDLE_EOF
return 0, .EOF
} else {
return int(bytes_read), ERROR_NONE
return int(bytes_read), nil
}
} 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
switch whence {
case 0: w = win32.FILE_BEGIN
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
lo := i32(offset)
ft := win32.GetFileType(win32.HANDLE(fd))
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)
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := Errno(win32.GetLastError())
err := get_last_error()
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
err: Errno
err: Error
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
return i64(length), err
}
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
@(private)
MAX_RW :: 1<<30
ERROR_EOF :: 38
@(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data
if len(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)
done: win32.DWORD
e: Errno
e: Error
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError())
e = get_last_error()
done = 0
}
return int(done), e
}
@(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data
if len(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)
done: win32.DWORD
e: Errno
e: Error
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError())
e = get_last_error()
done = 0
}
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
*/
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 {
return 0, ERROR_NEGATIVE_OFFSET
return 0, .Invalid_Offset
}
b, offset := data, offset
for len(b) > 0 {
m, e := pread(fd, b, offset)
if e == ERROR_EOF {
err = 0
err = nil
break
}
if e != 0 {
if e != nil {
err = e
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
*/
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 {
return 0, ERROR_NEGATIVE_OFFSET
return 0, .Invalid_Offset
}
b, offset := data, offset
for len(b) > 0 {
m, e := pwrite(fd, b, offset)
if e != 0 {
err = e
break
}
m := pwrite(fd, b, offset) or_return
n += m
b = b[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))
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h))
return Handle(fd)
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
return attribs != win32.INVALID_FILE_ATTRIBUTES
}
@(require_results)
is_file :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
return false
}
@(require_results)
is_dir :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
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
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
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))
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 ""
}
set_current_directory :: proc(path: string) -> (err: Errno) {
set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
win32.AcquireSRWLockExclusive(&cwd_lock)
if !win32.SetCurrentDirectoryW(wstr) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
win32.ReleaseSRWLockExclusive(&cwd_lock)
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
}
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()
// Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.CreateDirectoryW(wpath, nil) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
return
}
remove_directory :: proc(path: string) -> (err: Errno) {
remove_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.RemoveDirectoryW(wpath) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
return
}
@(private)
@(private, require_results)
is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' {
return true
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
return false
}
@(private)
@(private, require_results)
fix_long_path :: proc(path: string) -> string {
if len(path) < 248 {
return path
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
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)
n := len(path)
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()
n := win32.utf8_to_wstring(fix_long_path(new_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()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.DeleteFileW(wpath) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
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()
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
return
}
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
curr_off, e := seek(fd, 0, 1)
if e != 0 {
return e
}
ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
curr_off := seek(fd, 0, 1) or_return
defer seek(fd, curr_off, 0)
_, e = seek(fd, length, 0)
if e != 0 {
return e
}
_= seek(fd, length, 0) or_return
ok := win32.SetEndOfFile(win32.HANDLE(fd))
if !ok {
return Errno(win32.GetLastError())
return get_last_error()
}
return ERROR_NONE
return nil
}
truncate :: proc(path: string, length: i64) -> (err: Errno) {
fd: Handle
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
if err != 0 {
return
}
truncate :: proc(path: string, length: i64) -> (err: Error) {
fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
defer close(fd)
err = ftruncate(fd, length)
return
return ftruncate(fd, length)
}
remove :: proc(name: string) -> Errno {
remove :: proc(name: string) -> Error {
p := win32.utf8_to_wstring(fix_long_path(name))
err, err1: win32.DWORD
if !win32.DeleteFileW(p) {
err = win32.GetLastError()
}
if err == 0 {
return 0
return nil
}
if !win32.RemoveDirectoryW(p) {
err1 = win32.GetLastError()
}
if err1 == 0 {
return 0
return nil
}
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.nLength = size_of(win32.SECURITY_ATTRIBUTES)
sa.bInheritHandle = true
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
err = Errno(win32.GetLastError())
err = get_last_error()
}
return
}
+98 -66
View File
@@ -1,6 +1,8 @@
package os
import "base:intrinsics"
import "base:runtime"
import "core:io"
import "core:strconv"
import "core:unicode/utf8"
@@ -13,15 +15,15 @@ SEEK_SET :: 0
SEEK_CUR :: 1
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)
}
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
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 {
return write_byte(fd, byte(r))
}
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
return write(fd, b[:n])
}
write_encoded_rune :: proc(fd: Handle, r: rune) {
write_byte(fd, '\'')
write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
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 {
case '\a': write_string(fd, "\\a")
case '\b': write_string(fd, "\\b")
case '\e': write_string(fd, "\\e")
case '\f': write_string(fd, "\\f")
case '\n': write_string(fd, "\\n")
case '\r': write_string(fd, "\\r")
case '\t': write_string(fd, "\\t")
case '\v': write_string(fd, "\\v")
case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
case:
if r < 32 {
write_string(fd, "\\x")
if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) {
case 0: write_string(fd, "00")
case 1: write_rune(fd, '0')
case 2: write_string(fd, s)
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
case 2: if wrap(write_string(f, s), &n, &err) { return }
}
} 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 {
return 0, -1
return 0, io.Error.Short_Buffer
}
nn := max(int)
for nn > 0 && n < min && err == 0 {
for nn > 0 && n < min && err == nil {
nn, err = read(fd, buf[n:])
n += nn
}
if n >= min {
err = 0
err = nil
}
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))
}
@(require_results)
file_size_from_path :: proc(path: string) -> i64 {
fd, err := open(path, O_RDONLY, 0)
if err != 0 {
if err != nil {
return -1
}
defer close(fd)
length: i64
if length, err = file_size(fd); err != 0 {
if length, err = file_size(fd); err != nil {
return -1
}
return length
}
@(require_results)
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator
fd, err := open(name, O_RDONLY, 0)
if err != 0 {
return nil, false
}
defer close(fd)
return read_entire_file_from_handle(fd, allocator, loc)
err: Error
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
success = err == nil
return
}
@(require_results)
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator
length: i64
err: Errno
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
err: Error
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
success = err == nil
return
}
read_entire_file :: proc {
@@ -136,7 +127,50 @@ read_entire_file :: proc {
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) {
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
if truncate {
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
}
fd, err := open(name, flags, mode)
if err != 0 {
return false
}
fd := open(name, flags, mode) or_return
defer close(fd)
_, write_err := write(fd, data)
return write_err == 0
_ = write(fd, data) or_return
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])
}
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])
}
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
heap_resize :: runtime.heap_resize
heap_free :: runtime.heap_free
@(require_results)
processor_core_count :: proc() -> int {
return _processor_core_count()
}
+27 -11
View File
@@ -10,20 +10,29 @@ file_allocator :: proc() -> runtime.Allocator {
temp_allocator_proc :: runtime.arena_allocator_proc
@(private="file")
MAX_TEMP_ARENA_COUNT :: 2
@(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)
temp_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{
procedure = temp_allocator_proc,
data = &global_default_temp_allocator_arena,
data = &global_default_temp_allocator_arenas[global_default_temp_allocator_index],
}
}
@(require_results)
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
}
@@ -33,16 +42,23 @@ temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_locatio
@(fini, private)
temp_allocator_fini :: proc() {
runtime.arena_destroy(&global_default_temp_allocator_arena)
global_default_temp_allocator_arena = {}
for &arena in global_default_temp_allocator_arenas {
runtime.arena_destroy(&arena)
}
global_default_temp_allocator_arenas = {}
}
@(deferred_out=temp_allocator_temp_end)
TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
if ignore {
return {}, loc
} else {
return temp_allocator_temp_begin(loc), loc
TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
runtime.arena_temp_end(temp, loc)
if temp.arena != nil {
global_default_temp_allocator_index = (global_default_temp_allocator_index-1)%MAX_TEMP_ARENA_COUNT
}
}
@(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 "core:slice"
read_dir :: read_directory
@(require_results)
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
if f == nil {
@@ -57,6 +59,7 @@ read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -
}
Read_Directory_Iterator :: struct {
f: ^File,
impl: Read_Directory_Iterator_Impl,
@@ -72,7 +75,6 @@ read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
_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`
@(require_results)
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.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
defer win32.CloseHandle(handle)
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
#assert(size_of(fi.inode) == 16)
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
}
return
}
@@ -46,6 +57,8 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
return
}
TEMP_ALLOCATOR_GUARD()
for !it.impl.no_more_files {
err: Error
file_info_delete(it.impl.prev_fi, file_allocator())
+2 -1
View File
@@ -42,13 +42,14 @@ Error :: union #shared_nil {
ERROR_NONE :: Error{}
@(require_results)
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
@(require_results)
error_string :: proc(ferr: Error) -> string {
if ferr == nil {
return ""
+2 -2
View File
@@ -4,8 +4,8 @@ package os2
import "core:sys/linux"
@(rodata)
_errno_strings : [linux.Errno]string = {
.NONE = "Success",
_errno_strings := [linux.Errno]string{
.NONE = "",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.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)
}
// @(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)
new_file :: proc(handle: uintptr, name: string) -> ^File {
return _new_file(handle, name) or_else panic("Out of memory")
+72 -10
View File
@@ -1,9 +1,10 @@
//+private
package os2
import "base:runtime"
import "core:io"
import "core:time"
import "base:runtime"
import "core:sync"
import "core:sys/linux"
File_Impl :: struct {
@@ -11,6 +12,10 @@ File_Impl :: struct {
name: string,
fd: linux.Fd,
allocator: runtime.Allocator,
buffer: []byte,
rw_mutex: sync.RW_Mutex, // read write calls
p_mutex: sync.Mutex, // pread pwrite calls
}
_stdin := File{
@@ -49,9 +54,9 @@ _standard_stream_init :: proc() {
fd = 2,
}
stdin_impl.allocator = _file_allocator()
stdout_impl.allocator = _file_allocator()
stderr_impl.allocator = _file_allocator()
stdin_impl.allocator = file_allocator()
stdout_impl.allocator = file_allocator()
stderr_impl.allocator = file_allocator()
_stdin.impl = &stdin_impl
_stdout.impl = &stdout_impl
@@ -67,10 +72,6 @@ _standard_stream_init :: proc() {
stderr = &_stderr
}
_file_allocator :: proc() -> runtime.Allocator {
return heap_allocator()
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
TEMP_ALLOCATOR_GUARD()
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
}
@(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 {
if f == nil {
return nil
}
a := f.allocator
delete(f.name, a)
free(f, a)
err0 := delete(f.name, a)
err1 := delete(f.buffer, a)
err2 := free(f, a)
err0 or_return
err1 or_return
err2 or_return
return nil
}
@@ -470,3 +488,47 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
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
}
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) {
return write(f, ([^]byte)(data)[:len])
@@ -155,11 +173,8 @@ write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := tru
if truncate {
flags |= O_TRUNC
}
f, err := open(name, flags, perm)
if err != nil {
return err
}
_, err = write(f, data)
f := open(name, flags, perm) or_return
_, err := write(f, data)
if cerr := close(f); cerr != nil && err == nil {
err = cerr
}
+58 -10
View File
@@ -33,6 +33,11 @@ File_Impl :: struct {
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
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
return
}
TEMP_ALLOCATOR_GUARD()
path := _fix_long_path(name) or_return
path := _fix_long_path(name, temp_allocator()) or_return
access: u32
switch flags & {.Read, .Write} {
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
}
@(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 {
if f == nil || f.impl == nil {
return INVALID_HANDLE
@@ -180,9 +206,13 @@ _destroy :: proc(f: ^File_Impl) -> Error {
err0 := free(f.wname, a)
err1 := delete(f.name, a)
err2 := free(f, a)
err3 := delete(f.r_buf, a)
err4 := delete(f.w_buf, a)
err0 or_return
err1 or_return
err2 or_return
err3 or_return
err4 or_return
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) {
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) {
if len(b) == 0 {
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) {
return _write_internal(f, p)
}
_write_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
if len(p) == 0 {
return
}
@@ -435,6 +472,9 @@ _sync :: proc(f: ^File) -> Error {
}
_flush :: proc(f: ^File_Impl) -> Error {
return _flush(f)
}
_flush_internal :: proc(f: ^File_Impl) -> Error {
handle := _handle(&f.file)
if !win32.FlushFileBuffers(handle) {
return _get_platform_error()
@@ -457,7 +497,8 @@ _truncate :: proc(f: ^File, size: i64) -> 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
if !win32.DeleteFileW(p) {
err = _get_platform_error()
@@ -494,8 +535,9 @@ _remove :: proc(name: string) -> Error {
}
_rename :: proc(old_path, new_path: string) -> Error {
from := _fix_long_path(old_path) or_return
to := _fix_long_path(new_path) or_return
TEMP_ALLOCATOR_GUARD()
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) {
return nil
}
@@ -504,8 +546,9 @@ _rename :: proc(old_path, new_path: string) -> Error {
}
_link :: proc(old_name, new_name: string) -> Error {
o := _fix_long_path(old_name) or_return
n := _fix_long_path(new_name) or_return
TEMP_ALLOCATOR_GUARD()
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) {
return nil
}
@@ -592,8 +635,10 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er
@thread_local
rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
p := _fix_long_path(name) or_return
handle := _open_sym_link(p) or_return
TEMP_ALLOCATOR_GUARD()
p := _fix_long_path(name, temp_allocator()) or_return
handle := _open_sym_link(p) or_return
defer win32.CloseHandle(handle)
bytes_returned: u32
@@ -667,7 +712,8 @@ _fchown :: proc(f: ^File, uid, gid: int) -> 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) {
return _get_platform_error()
}
@@ -723,7 +769,8 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
}
_exists :: proc(path: string) -> bool {
wpath, _ := _fix_long_path(path)
TEMP_ALLOCATOR_GUARD()
wpath, _ := _fix_long_path(path, temp_allocator())
attribs := win32.GetFileAttributesW(wpath)
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)
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)
+6 -5
View File
@@ -13,7 +13,8 @@ _is_path_separator :: proc(c: byte) -> bool {
}
_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 nil
@@ -169,13 +170,13 @@ init_long_path_support :: proc() {
}
@(require_results)
_fix_long_path_slice :: proc(path: string) -> ([]u16, runtime.Allocator_Error) {
return win32_utf8_to_utf16(_fix_long_path_internal(path), temp_allocator())
_fix_long_path_slice :: proc(path: string, allocator: runtime.Allocator) -> ([]u16, runtime.Allocator_Error) {
return win32_utf8_to_utf16(_fix_long_path_internal(path), allocator)
}
@(require_results)
_fix_long_path :: proc(path: string) -> (win32.wstring, runtime.Allocator_Error) {
return win32_utf8_to_wstring(_fix_long_path_internal(path), temp_allocator())
_fix_long_path :: proc(path: string, allocator: runtime.Allocator) -> (win32.wstring, runtime.Allocator_Error) {
return win32_utf8_to_wstring(_fix_long_path_internal(path), allocator)
}
@(require_results)
+2 -2
View File
@@ -12,8 +12,8 @@ File_Info :: struct {
name: string,
inode: u128, // might be zero if cannot be determined
size: i64,
mode: int,
size: i64 `fmt:"M"`,
mode: int `fmt:"o"`,
type: File_Type,
creation_time: time.Time,
+7 -9
View File
@@ -6,25 +6,22 @@ import "core:time"
import "core:strings"
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 {
return {}, nil
return
}
path, err := _cleanpath_from_handle(f, allocator)
if err != nil {
return {}, err
}
path := _cleanpath_from_handle(f, allocator) or_return
h := _handle(f)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi := File_Info {
fi = File_Info {
fullpath = path,
name = basename(path),
type = file_type(h),
}
return fi, nil
return
}
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 {
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
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
+7 -4
View File
@@ -3,8 +3,11 @@ package os2
import "base:runtime"
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
//TODO
return "", nil
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
TEMP_ALLOCATOR_GUARD()
tmpdir := get_env("TMPDIR", temp_allocator())
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"
Handle :: distinct int;
Errno :: distinct int;
Handle :: distinct int
_Platform_Error :: enum i32 {NONE}
ERROR_NONE :: (Errno) (es.SUCCESS);
// ERROR_NONE :: Error(es.SUCCESS)
O_RDONLY :: 0x1;
O_WRONLY :: 0x2;
O_CREATE :: 0x4;
O_TRUNC :: 0x8;
O_RDONLY :: 0x1
O_WRONLY :: 0x2
O_CREATE :: 0x4
O_TRUNC :: 0x8
stderr : Handle = 0;
stderr : Handle = 0
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 {
return es.HeapAllocate(size, zero_memory);
return es.HeapAllocate(size, zero_memory)
}
heap_free :: proc(ptr: rawptr) {
es.HeapFree(ptr);
es.HeapFree(ptr)
}
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) {
return (Handle) (0), (Errno) (1);
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
return (Handle) (0), (Error) (1)
}
close :: proc(fd: Handle) -> Errno {
return (Errno) (1);
close :: proc(fd: Handle) -> Error {
return (Error) (1)
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
return (i64) (0), (Errno) (1);
file_size :: proc(fd: Handle) -> (i64, Error) {
return (i64) (0), (Error) (1)
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return (int) (0), (Errno) (1);
read :: proc(fd: Handle, data: []byte) -> (int, Error) {
return (int) (0), (Error) (1)
}
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
return (int) (0), (Errno) (1);
write :: proc(fd: Handle, data: []u8) -> (int, Error) {
return (int) (0), (Error) (1)
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
return (i64) (0), (Errno) (1);
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
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
File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0
EPERM: Errno : 1
ENOENT: Errno : 2
ESRCH: Errno : 3
EINTR: Errno : 4
EIO: Errno : 5
ENXIO: Errno : 6
E2BIG: Errno : 7
ENOEXEC: Errno : 8
EBADF: Errno : 9
ECHILD: Errno : 10
EBEADLK: Errno : 11
ENOMEM: Errno : 12
EACCESS: Errno : 13
EFAULT: Errno : 14
ENOTBLK: Errno : 15
EBUSY: Errno : 16
EEXIST: Errno : 17
EXDEV: Errno : 18
ENODEV: Errno : 19
ENOTDIR: Errno : 20
EISDIR: Errno : 21
EINVAL: Errno : 22
ENFILE: Errno : 23
EMFILE: Errno : 24
ENOTTY: Errno : 25
ETXTBSY: Errno : 26
EFBIG: Errno : 27
ENOSPC: Errno : 28
ESPIPE: Errno : 29
EROFS: Errno : 30
EMLINK: Errno : 31
EPIPE: Errno : 32
EDOM: Errno : 33
ERANGE: Errno : 34 /* Result too large */
EAGAIN: Errno : 35
EINPROGRESS: Errno : 36
EALREADY: Errno : 37
ENOTSOCK: Errno : 38
EDESTADDRREQ: Errno : 39
EMSGSIZE: Errno : 40
EPROTOTYPE: Errno : 41
ENOPROTOOPT: Errno : 42
EPROTONOSUPPORT: Errno : 43
ESOCKTNOSUPPORT: Errno : 44
EOPNOTSUPP: Errno : 45
EPFNOSUPPORT: Errno : 46
EAFNOSUPPORT: Errno : 47
EADDRINUSE: Errno : 48
EADDRNOTAVAIL: Errno : 49
ENETDOWN: Errno : 50
ENETUNREACH: Errno : 51
ENETRESET: Errno : 52
ECONNABORTED: Errno : 53
ECONNRESET: Errno : 54
ENOBUFS: Errno : 55
EISCONN: Errno : 56
ENOTCONN: Errno : 57
ESHUTDOWN: Errno : 58
ETIMEDOUT: Errno : 60
ECONNREFUSED: Errno : 61
ELOOP: Errno : 62
ENAMETOOLING: Errno : 63
EHOSTDOWN: Errno : 64
EHOSTUNREACH: Errno : 65
ENOTEMPTY: Errno : 66
EPROCLIM: Errno : 67
EUSERS: Errno : 68
EDQUOT: Errno : 69
ESTALE: Errno : 70
EBADRPC: Errno : 72
ERPCMISMATCH: Errno : 73
EPROGUNAVAIL: Errno : 74
EPROGMISMATCH: Errno : 75
EPROCUNAVAIL: Errno : 76
ENOLCK: Errno : 77
ENOSYS: Errno : 78
EFTYPE: Errno : 79
EAUTH: Errno : 80
ENEEDAUTH: Errno : 81
EIDRM: Errno : 82
ENOMSG: Errno : 83
EOVERFLOW: Errno : 84
ECANCELED: Errno : 85
EILSEQ: Errno : 86
ENOATTR: Errno : 87
EDOOFUS: Errno : 88
EBADMSG: Errno : 89
EMULTIHOP: Errno : 90
ENOLINK: Errno : 91
EPROTO: Errno : 92
ENOTCAPABLE: Errno : 93
ECAPMODE: Errno : 94
ENOTRECOVERABLE: Errno : 95
EOWNERDEAD: Errno : 96
_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,
EBEADLK = 11,
ENOMEM = 12,
EACCESS = 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, /* Result too large */
EAGAIN = 35,
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,
ETIMEDOUT = 60,
ECONNREFUSED = 61,
ELOOP = 62,
ENAMETOOLING = 63,
EHOSTDOWN = 64,
EHOSTUNREACH = 65,
ENOTEMPTY = 66,
EPROCLIM = 67,
EUSERS = 68,
EDQUOT = 69,
ESTALE = 70,
EBADRPC = 72,
ERPCMISMATCH = 73,
EPROGUNAVAIL = 74,
EPROGMISMATCH = 75,
EPROCUNAVAIL = 76,
ENOLCK = 77,
ENOSYS = 78,
EFTYPE = 79,
EAUTH = 80,
ENEEDAUTH = 81,
EIDRM = 82,
ENOMSG = 83,
EOVERFLOW = 84,
ECANCELED = 85,
EILSEQ = 86,
ENOATTR = 87,
EDOOFUS = 88,
EBADMSG = 89,
EMULTIHOP = 90,
ENOLINK = 91,
EPROTO = 92,
ENOTCAPABLE = 93,
ECAPMODE = 94,
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_WRONLY :: 0x00001
@@ -258,13 +353,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete
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 }
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 }
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 }
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
@(require_results) S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
@(require_results) S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
@(require_results) S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
@(require_results) S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
@(require_results) S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
@(require_results) S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
@(require_results) S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission
@@ -274,7 +369,7 @@ R_OK :: 4 // Test for read permission
F_KINFO :: 22
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="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 ---
}
@(require_results)
is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc "contextless" () -> int {
return int(__errno_location()^)
@(require_results, no_instrumentation)
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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
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)
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`.
@@ -354,124 +457,148 @@ close :: proc(fd: Handle) -> Errno {
@(private)
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)
bytes_read := _unix_read(fd, &data[0], to_read)
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 {
return 0, ERROR_NONE
return 0, nil
}
to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write)
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))
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) {
s, err := _fstat(fd)
if err != ERROR_NONE {
return -1, err
}
return s.size, ERROR_NONE
@(require_results)
file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
size = -1
s := _fstat(fd) or_return
size = s.size
return
}
rename :: proc(old_path, new_path: string) -> Errno {
rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_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)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
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 {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISDIR(s.mode)
}
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISDIR(s.mode)
@@ -490,38 +617,40 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> 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)
if err != ERROR_NONE {
if err != nil {
return 0, err
}
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)
if err != ERROR_NONE {
if err != nil {
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE
return File_Time(modified), nil
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
if result == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -529,54 +658,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = ---
res := _unix_lstat(cstr, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
@(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = ---
result := _unix_fstat(fd, &s)
if result == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
@(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil {
return nil, Errno(get_last_error())
return nil, get_last_error()
}
return dirp, ERROR_NONE
return dirp, nil
}
@private
_closedir :: proc(dirp: Dir) -> Errno {
@(private)
_closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp)
if rc != 0 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
@private
@(private)
_rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp)
}
@private
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
@(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 {
err = Errno(get_last_error())
err = get_last_error()
return
}
err = ERROR_NONE
if result == nil {
end_of_stream = true
@@ -586,8 +714,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return
}
@private
_readlink :: proc(path: string) -> (string, Errno) {
@(private, require_results)
_readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
if rc == -1 {
delete(buf)
return "", Errno(get_last_error())
return "", get_last_error()
} else if rc == int(bufsz) {
bufsz += MAX_PATH
delete(buf)
buf = make([]byte, bufsz)
} 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
// 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
@@ -626,14 +755,15 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo)
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))
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
if rel == "" {
rel = "."
@@ -644,27 +774,28 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
return "", Errno(get_last_error())
return "", get_last_error()
}
defer _unix_free(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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask))
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) {
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
}
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems.
@@ -692,7 +825,7 @@ get_current_directory :: proc() -> string {
if cwd != nil {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
if get_last_error() != ERANGE {
delete(buf)
return ""
}
@@ -701,14 +834,14 @@ get_current_directory :: proc() -> string {
unreachable()
}
set_current_directory :: proc(path: string) -> (err: Errno) {
set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
exit :: proc "contextless" (code: int) -> ! {
@@ -716,16 +849,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code))
}
@(require_results)
current_thread_id :: proc "contextless" () -> int {
return cast(int) pthread_getthreadid_np()
}
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -741,6 +877,7 @@ dlerror :: proc() -> string {
return string(_unix_dlerror())
}
@(require_results)
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@@ -753,7 +890,7 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
count : int = 0
count_size := size_of(count)
@@ -767,6 +904,7 @@ _processor_core_count :: proc() -> int {
}
@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
+101 -69
View File
@@ -10,16 +10,14 @@ import "core:sys/haiku"
Handle :: i32
Pid :: i32
File_Time :: i64
Errno :: i32
_Platform_Error :: haiku.Errno
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)
ERROR_NONE: Errno: 0
stdin: Handle = 0
stdout: Handle = 1
stderr: Handle = 2
@@ -121,7 +119,7 @@ S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK
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="getthrid") _unix_getthrid :: proc() -> int ---
@@ -179,38 +177,47 @@ Dirent :: struct {
Dir :: distinct rawptr // DIR*
@(require_results)
is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc "contextless" () -> int {
return int(__error()^)
@(require_results, no_instrumentation)
get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__error()^)
}
fork :: proc() -> (Pid, Errno) {
@(require_results)
fork :: proc() -> (Pid, Error) {
pid := _unix_fork()
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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
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)
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,
@@ -220,47 +227,69 @@ close :: proc(fd: Handle) -> Errno {
@(private)
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)
bytes_read := _unix_read(fd, &data[0], to_read)
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 {
return 0, ERROR_NONE
return 0, nil
}
to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write)
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))
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)
if err != ERROR_NONE {
if err != nil {
return -1, err
}
return s.size, ERROR_NONE
return s.size, nil
}
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments()
@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
@@ -269,8 +298,8 @@ _alloc_command_line_arguments :: proc() -> []string {
return res
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -278,13 +307,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = ---
res := _unix_stat(cstr, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -292,55 +321,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = ---
res := _unix_lstat(cstr, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
@(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized
s: OS_Stat = ---
res := _unix_fstat(fd, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
@(private)
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil {
return nil, Errno(get_last_error())
return nil, get_last_error()
}
return dirp, ERROR_NONE
return dirp, nil
}
@private
_closedir :: proc(dirp: Dir) -> Errno {
@(private)
_closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp)
if rc != 0 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
@private
@(private)
_rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp)
}
@private
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
@(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 {
err = Errno(get_last_error())
err = get_last_error()
return
}
err = ERROR_NONE
if result == nil {
end_of_stream = true
@@ -350,8 +378,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return
}
@private
_readlink :: proc(path: string) -> (string, Errno) {
@(private, require_results)
_readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
if rc == -1 {
delete(buf)
return "", Errno(get_last_error())
return "", get_last_error()
} else if rc == int(bufsz) {
bufsz += MAX_PATH
delete(buf)
buf = make([]byte, bufsz)
} 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) {
return "", Errno(ENOSYS)
@(require_results)
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
if rel == "" {
rel = "."
@@ -387,26 +417,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
return "", Errno(get_last_error())
return "", get_last_error()
}
defer _unix_free(path_ptr)
path_cstr := transmute(cstring)path_ptr
path = strings.clone( string(path_cstr) )
path_cstr := cstring(path_ptr)
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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask))
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) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == 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
}
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
info: haiku.system_info
haiku.get_system_info(&info)
+105 -60
View File
@@ -3,42 +3,45 @@ package os
import "base:runtime"
@(require_results)
is_path_separator :: proc(c: byte) -> bool {
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")
}
close :: proc(fd: Handle) -> Errno {
close :: proc(fd: Handle) -> Error {
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")
}
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")
}
@(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")
}
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")
}
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")
}
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")
}
@@ -47,38 +50,42 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
MAX_RW :: 1<<30
@(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")
}
@(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")
}
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")
}
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")
}
stdout: Handle = 1
stderr: Handle = 2
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
}
@(require_results)
exists :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
@(require_results)
is_file :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
@(require_results)
is_dir :: proc(path: string) -> bool {
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
//@private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string {
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")
}
change_directory :: proc(path: string) -> (err: Errno) {
change_directory :: proc(path: string) -> (err: Error) {
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")
}
remove_directory :: proc(path: string) -> (err: Errno) {
remove_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
@(private, require_results)
is_abs :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
@(private, require_results)
fix_long_path :: proc(path: string) -> string {
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")
}
unlink :: proc(path: string) -> (err: Errno) {
unlink :: proc(path: string) -> (err: Error) {
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")
}
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")
}
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")
}
remove :: proc(name: string) -> Errno {
remove :: proc(name: string) -> Error {
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")
}
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")
}
Handle :: distinct uintptr
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)
@@ -182,37 +225,34 @@ O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000
ERROR_NONE: Errno : 0
ERROR_FILE_NOT_FOUND: Errno : 2
ERROR_PATH_NOT_FOUND: Errno : 3
ERROR_ACCESS_DENIED: Errno : 5
ERROR_INVALID_HANDLE: Errno : 6
ERROR_NOT_ENOUGH_MEMORY: Errno : 8
ERROR_NO_MORE_FILES: Errno : 18
ERROR_HANDLE_EOF: Errno : 38
ERROR_NETNAME_DELETED: Errno : 64
ERROR_FILE_EXISTS: Errno : 80
ERROR_INVALID_PARAMETER: Errno : 87
ERROR_BROKEN_PIPE: Errno : 109
ERROR_BUFFER_OVERFLOW: Errno : 111
ERROR_INSUFFICIENT_BUFFER: Errno : 122
ERROR_MOD_NOT_FOUND: Errno : 126
ERROR_PROC_NOT_FOUND: Errno : 127
ERROR_DIR_NOT_EMPTY: Errno : 145
ERROR_ALREADY_EXISTS: Errno : 183
ERROR_ENVVAR_NOT_FOUND: Errno : 203
ERROR_MORE_DATA: Errno : 234
ERROR_OPERATION_ABORTED: Errno : 995
ERROR_IO_PENDING: Errno : 997
ERROR_NOT_FOUND: Errno : 1168
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314
WSAEACCES: Errno : 10013
WSAECONNRESET: Errno : 10054
ERROR_FILE_NOT_FOUND :: Platform_Error.FILE_NOT_FOUND
ERROR_PATH_NOT_FOUND :: Platform_Error.PATH_NOT_FOUND
ERROR_ACCESS_DENIED :: Platform_Error.ACCESS_DENIED
ERROR_INVALID_HANDLE :: Platform_Error.INVALID_HANDLE
ERROR_NOT_ENOUGH_MEMORY :: Platform_Error.NOT_ENOUGH_MEMORY
ERROR_NO_MORE_FILES :: Platform_Error.NO_MORE_FILES
ERROR_HANDLE_EOF :: Platform_Error.HANDLE_EOF
ERROR_NETNAME_DELETED :: Platform_Error.NETNAME_DELETED
ERROR_FILE_EXISTS :: Platform_Error.FILE_EXISTS
ERROR_INVALID_PARAMETER :: Platform_Error.INVALID_PARAMETER
ERROR_BROKEN_PIPE :: Platform_Error.BROKEN_PIPE
ERROR_BUFFER_OVERFLOW :: Platform_Error.BUFFER_OVERFLOW
ERROR_INSUFFICIENT_BUFFER :: Platform_Error.INSUFFICIENT_BUFFER
ERROR_MOD_NOT_FOUND :: Platform_Error.MOD_NOT_FOUND
ERROR_PROC_NOT_FOUND :: Platform_Error.PROC_NOT_FOUND
ERROR_DIR_NOT_EMPTY :: Platform_Error.DIR_NOT_EMPTY
ERROR_ALREADY_EXISTS :: Platform_Error.ALREADY_EXISTS
ERROR_ENVVAR_NOT_FOUND :: Platform_Error.ENVVAR_NOT_FOUND
ERROR_MORE_DATA :: Platform_Error.MORE_DATA
ERROR_OPERATION_ABORTED :: Platform_Error.OPERATION_ABORTED
ERROR_IO_PENDING :: Platform_Error.IO_PENDING
ERROR_NOT_FOUND :: Platform_Error.NOT_FOUND
ERROR_PRIVILEGE_NOT_HELD :: Platform_Error.PRIVILEGE_NOT_HELD
WSAEACCES :: Platform_Error.WSAEACCES
WSAECONNRESET :: Platform_Error.WSAECONNRESET
// Windows reserves errors >= 1<<29 for application use
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
// "Argv" arguments converted to Odin strings
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")
}
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")
}
@(require_results)
get_page_size :: proc() -> int {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
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 {
context = runtime.default_context()
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 {
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
// and have people's code break while it's still work in progress.
import unix "core:sys/unix"
import linux "core:sys/linux"
Handle :: distinct i32
Pid :: distinct i32
File_Time :: distinct u64
Errno :: distinct i32
Socket :: distinct int
INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0
EPERM: Errno : 1
ENOENT: Errno : 2
ESRCH: Errno : 3
EINTR: Errno : 4
EIO: Errno : 5
ENXIO: Errno : 6
EBADF: Errno : 9
EAGAIN: Errno : 11
ENOMEM: Errno : 12
EACCES: Errno : 13
EFAULT: Errno : 14
EEXIST: Errno : 17
ENODEV: Errno : 19
ENOTDIR: Errno : 20
EISDIR: Errno : 21
EINVAL: Errno : 22
ENFILE: Errno : 23
EMFILE: Errno : 24
ETXTBSY: Errno : 26
EFBIG: Errno : 27
ENOSPC: Errno : 28
ESPIPE: Errno : 29
EROFS: Errno : 30
EPIPE: Errno : 32
_Platform_Error :: linux.Errno
EPERM :: Platform_Error.EPERM
ENOENT :: Platform_Error.ENOENT
ESRCH :: Platform_Error.ESRCH
EINTR :: Platform_Error.EINTR
EIO :: Platform_Error.EIO
ENXIO :: Platform_Error.ENXIO
EBADF :: Platform_Error.EBADF
EAGAIN :: Platform_Error.EAGAIN
ENOMEM :: Platform_Error.ENOMEM
EACCES :: Platform_Error.EACCES
EFAULT :: Platform_Error.EFAULT
EEXIST :: Platform_Error.EEXIST
ENODEV :: Platform_Error.ENODEV
ENOTDIR :: Platform_Error.ENOTDIR
EISDIR :: Platform_Error.EISDIR
EINVAL :: Platform_Error.EINVAL
ENFILE :: Platform_Error.ENFILE
EMFILE :: Platform_Error.EMFILE
ETXTBSY :: Platform_Error.ETXTBSY
EFBIG :: Platform_Error.EFBIG
ENOSPC :: Platform_Error.ENOSPC
ESPIPE :: Platform_Error.ESPIPE
EROFS :: Platform_Error.EROFS
EPIPE :: Platform_Error.EPIPE
ERANGE: Errno : 34 /* Result too large */
EDEADLK: Errno : 35 /* Resource deadlock would occur */
ENAMETOOLONG: Errno : 36 /* File name too long */
ENOLCK: Errno : 37 /* No record locks available */
ERANGE :: Platform_Error.ERANGE /* Result too large */
EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock would occur */
ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
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 */
ELOOP: Errno : 40 /* Too many symbolic links encountered */
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */
ENOMSG: Errno : 42 /* No message of desired type */
EIDRM: Errno : 43 /* Identifier removed */
ECHRNG: Errno : 44 /* Channel number out of range */
EL2NSYNC: Errno : 45 /* Level 2 not synchronized */
EL3HLT: Errno : 46 /* Level 3 halted */
EL3RST: Errno : 47 /* Level 3 reset */
ELNRNG: Errno : 48 /* Link number out of range */
EUNATCH: Errno : 49 /* Protocol driver not attached */
ENOCSI: Errno : 50 /* No CSI structure available */
EL2HLT: Errno : 51 /* Level 2 halted */
EBADE: Errno : 52 /* Invalid exchange */
EBADR: Errno : 53 /* Invalid request descriptor */
EXFULL: Errno : 54 /* Exchange full */
ENOANO: Errno : 55 /* No anode */
EBADRQC: Errno : 56 /* Invalid request code */
EBADSLT: Errno : 57 /* Invalid slot */
EDEADLOCK: Errno : EDEADLK
EBFONT: Errno : 59 /* Bad font file format */
ENOSTR: Errno : 60 /* Device not a stream */
ENODATA: Errno : 61 /* No data available */
ETIME: Errno : 62 /* Timer expired */
ENOSR: Errno : 63 /* Out of streams resources */
ENONET: Errno : 64 /* Machine is not on the network */
ENOPKG: Errno : 65 /* Package not installed */
EREMOTE: Errno : 66 /* Object is remote */
ENOLINK: Errno : 67 /* Link has been severed */
EADV: Errno : 68 /* Advertise error */
ESRMNT: Errno : 69 /* Srmount error */
ECOMM: Errno : 70 /* Communication error on send */
EPROTO: Errno : 71 /* Protocol error */
EMULTIHOP: Errno : 72 /* Multihop attempted */
EDOTDOT: Errno : 73 /* RFS specific error */
EBADMSG: Errno : 74 /* Not a data message */
EOVERFLOW: Errno : 75 /* Value too large for defined data type */
ENOTUNIQ: Errno : 76 /* Name not unique on network */
EBADFD: Errno : 77 /* File descriptor in bad state */
EREMCHG: Errno : 78 /* Remote address changed */
ELIBACC: Errno : 79 /* Can not access a needed shared library */
ELIBBAD: Errno : 80 /* Accessing a corrupted shared library */
ELIBSCN: Errno : 81 /* .lib section in a.out corrupted */
ELIBMAX: Errno : 82 /* Attempting to link in too many shared libraries */
ELIBEXEC: Errno : 83 /* Cannot exec a shared library directly */
EILSEQ: Errno : 84 /* Illegal byte sequence */
ERESTART: Errno : 85 /* Interrupted system call should be restarted */
ESTRPIPE: Errno : 86 /* Streams pipe error */
EUSERS: Errno : 87 /* Too many users */
ENOTSOCK: Errno : 88 /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 89 /* Destination address required */
EMSGSIZE: Errno : 90 /* Message too long */
EPROTOTYPE: Errno : 91 /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 92 /* Protocol not available */
EPROTONOSUPPORT:Errno : 93 /* Protocol not supported */
ESOCKTNOSUPPORT:Errno : 94 /* Socket type not supported */
EOPNOTSUPP: Errno : 95 /* Operation not supported on transport endpoint */
EPFNOSUPPORT: Errno : 96 /* Protocol family not supported */
EAFNOSUPPORT: Errno : 97 /* Address family not supported by protocol */
EADDRINUSE: Errno : 98 /* Address already in use */
EADDRNOTAVAIL: Errno : 99 /* Cannot assign requested address */
ENETDOWN: Errno : 100 /* Network is down */
ENETUNREACH: Errno : 101 /* Network is unreachable */
ENETRESET: Errno : 102 /* Network dropped connection because of reset */
ECONNABORTED: Errno : 103 /* Software caused connection abort */
ECONNRESET: Errno : 104 /* Connection reset by peer */
ENOBUFS: Errno : 105 /* No buffer space available */
EISCONN: Errno : 106 /* Transport endpoint is already connected */
ENOTCONN: Errno : 107 /* Transport endpoint is not connected */
ESHUTDOWN: Errno : 108 /* Cannot send after transport endpoint shutdown */
ETOOMANYREFS: Errno : 109 /* Too many references: cannot splice */
ETIMEDOUT: Errno : 110 /* Connection timed out */
ECONNREFUSED: Errno : 111 /* Connection refused */
EHOSTDOWN: Errno : 112 /* Host is down */
EHOSTUNREACH: Errno : 113 /* No route to host */
EALREADY: Errno : 114 /* Operation already in progress */
EINPROGRESS: Errno : 115 /* Operation now in progress */
ESTALE: Errno : 116 /* Stale file handle */
EUCLEAN: Errno : 117 /* Structure needs cleaning */
ENOTNAM: Errno : 118 /* Not a XENIX named type file */
ENAVAIL: Errno : 119 /* No XENIX semaphores available */
EISNAM: Errno : 120 /* Is a named type file */
EREMOTEIO: Errno : 121 /* Remote I/O error */
EDQUOT: Errno : 122 /* Quota exceeded */
ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
ELOOP :: Platform_Error.ELOOP /* Too many symbolic links encountered */
EWOULDBLOCK :: Platform_Error.EWOULDBLOCK /* Operation would block */
ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
EIDRM :: Platform_Error.EIDRM /* Identifier removed */
ECHRNG :: Platform_Error.ECHRNG /* Channel number out of range */
EL2NSYNC :: Platform_Error.EL2NSYNC /* Level 2 not synchronized */
EL3HLT :: Platform_Error.EL3HLT /* Level 3 halted */
EL3RST :: Platform_Error.EL3RST /* Level 3 reset */
ELNRNG :: Platform_Error.ELNRNG /* Link number out of range */
EUNATCH :: Platform_Error.EUNATCH /* Protocol driver not attached */
ENOCSI :: Platform_Error.ENOCSI /* No CSI structure available */
EL2HLT :: Platform_Error.EL2HLT /* Level 2 halted */
EBADE :: Platform_Error.EBADE /* Invalid exchange */
EBADR :: Platform_Error.EBADR /* Invalid request descriptor */
EXFULL :: Platform_Error.EXFULL /* Exchange full */
ENOANO :: Platform_Error.ENOANO /* No anode */
EBADRQC :: Platform_Error.EBADRQC /* Invalid request code */
EBADSLT :: Platform_Error.EBADSLT /* Invalid slot */
EDEADLOCK :: Platform_Error.EDEADLOCK
EBFONT :: Platform_Error.EBFONT /* Bad font file format */
ENOSTR :: Platform_Error.ENOSTR /* Device not a stream */
ENODATA :: Platform_Error.ENODATA /* No data available */
ETIME :: Platform_Error.ETIME /* Timer expired */
ENOSR :: Platform_Error.ENOSR /* Out of streams resources */
ENONET :: Platform_Error.ENONET /* Machine is not on the network */
ENOPKG :: Platform_Error.ENOPKG /* Package not installed */
EREMOTE :: Platform_Error.EREMOTE /* Object is remote */
ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
EADV :: Platform_Error.EADV /* Advertise error */
ESRMNT :: Platform_Error.ESRMNT /* Srmount error */
ECOMM :: Platform_Error.ECOMM /* Communication error on send */
EPROTO :: Platform_Error.EPROTO /* Protocol error */
EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
EDOTDOT :: Platform_Error.EDOTDOT /* RFS specific error */
EBADMSG :: Platform_Error.EBADMSG /* Not a data message */
EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large for defined data type */
ENOTUNIQ :: Platform_Error.ENOTUNIQ /* Name not unique on network */
EBADFD :: Platform_Error.EBADFD /* File descriptor in bad state */
EREMCHG :: Platform_Error.EREMCHG /* Remote address changed */
ELIBACC :: Platform_Error.ELIBACC /* Can not access a needed shared library */
ELIBBAD :: Platform_Error.ELIBBAD /* Accessing a corrupted shared library */
ELIBSCN :: Platform_Error.ELIBSCN /* .lib section in a.out corrupted */
ELIBMAX :: Platform_Error.ELIBMAX /* Attempting to link in too many shared libraries */
ELIBEXEC :: Platform_Error.ELIBEXEC /* Cannot exec a shared library directly */
EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */
ERESTART :: Platform_Error.ERESTART /* Interrupted system call should be restarted */
ESTRPIPE :: Platform_Error.ESTRPIPE /* Streams pipe error */
EUSERS :: Platform_Error.EUSERS /* Too many users */
ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol not available */
EPROTONOSUPPOR :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
ESOCKTNOSUPPOR :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported on transport endpoint */
EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol */
EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Cannot assign requested address */
ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection because of reset */
ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
EISCONN :: Platform_Error.EISCONN /* Transport endpoint is already connected */
ENOTCONN :: Platform_Error.ENOTCONN /* Transport endpoint is not connected */
ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Cannot send after transport endpoint shutdown */
ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: cannot splice */
ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Connection timed out */
ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
ESTALE :: Platform_Error.ESTALE /* Stale file handle */
EUCLEAN :: Platform_Error.EUCLEAN /* Structure needs cleaning */
ENOTNAM :: Platform_Error.ENOTNAM /* Not a XENIX named type file */
ENAVAIL :: Platform_Error.ENAVAIL /* No XENIX semaphores available */
EISNAM :: Platform_Error.EISNAM /* Is a named type file */
EREMOTEIO :: Platform_Error.EREMOTEIO /* Remote I/O error */
EDQUOT :: Platform_Error.EDQUOT /* Quota exceeded */
ENOMEDIUM: Errno : 123 /* No medium found */
EMEDIUMTYPE: Errno : 124 /* Wrong medium type */
ECANCELED: Errno : 125 /* Operation Canceled */
ENOKEY: Errno : 126 /* Required key not available */
EKEYEXPIRED: Errno : 127 /* Key has expired */
EKEYREVOKED: Errno : 128 /* Key has been revoked */
EKEYREJECTED: Errno : 129 /* Key was rejected by service */
ENOMEDIUM :: Platform_Error.ENOMEDIUM /* No medium found */
EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE /* Wrong medium type */
ECANCELED :: Platform_Error.ECANCELED /* Operation Canceled */
ENOKEY :: Platform_Error.ENOKEY /* Required key not available */
EKEYEXPIRED :: Platform_Error.EKEYEXPIRED /* Key has expired */
EKEYREVOKED :: Platform_Error.EKEYREVOKED /* Key has been revoked */
EKEYREJECTED :: Platform_Error.EKEYREJECTED /* Key was rejected by service */
/* for robust mutexes */
EOWNERDEAD: Errno : 130 /* Owner died */
ENOTRECOVERABLE: Errno : 131 /* State not recoverable */
EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Owner died */
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
@@ -448,13 +448,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete
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 }
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 }
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 }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
@(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
@(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
@(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
@(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
@(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
@(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
@(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission
@@ -506,41 +506,55 @@ foreign dl {
@(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) ---
}
@(require_results)
is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
// determine errno from syscall return value
@private
_get_errno :: proc(res: int) -> Errno {
@(private, require_results)
_get_errno :: proc(res: int) -> Error {
if res < 0 && res > -4096 {
return Errno(-res)
return Platform_Error(-res)
}
return 0
return nil
}
// get errno from libc
get_last_error :: proc "contextless" () -> int {
return int(__errno_location()^)
@(require_results, no_instrumentation)
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)
if res == -1 {
return _get_errno(res)
}
return ERROR_NONE
return nil
}
fork :: proc() -> (Pid, Errno) {
@(require_results)
fork :: proc() -> (Pid, Error) {
pid := unix.sys_fork()
if pid == -1 {
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()
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))
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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := unix.sys_open(cstr, flags, uint(mode))
if handle < 0 {
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)))
}
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).
// `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,
@@ -578,9 +598,9 @@ close :: proc(fd: Handle) -> Errno {
@(private)
MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
read :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 {
return 0, ERROR_NONE
return 0, nil
}
to_read := min(uint(len(data)), MAX_RW)
@@ -589,12 +609,12 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if bytes_read < 0 {
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 {
return 0, ERROR_NONE
return 0, nil
}
to_write := min(uint(len(data)), MAX_RW)
@@ -603,12 +623,12 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if bytes_written < 0 {
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 {
return 0, ERROR_NONE
return 0, nil
}
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 {
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 {
return 0, ERROR_NONE
return 0, nil
}
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 {
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)
if res < 0 {
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
s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
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()
old_path_cstr := strings.clone_to_cstring(old_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))
}
remove :: proc(path: string) -> Errno {
remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_rmdir(path_cstr))
}
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISDIR(s.mode)
}
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
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_dir :: proc {is_dir_path, is_dir_handle}
@(require_results)
exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
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_by_name :: proc(name: string) -> File_Time {}
*/
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
s, err := _fstat(fd)
if err != ERROR_NONE {
return 0, err
}
@(require_results)
last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
s := _fstat(fd) or_return
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) {
s, err := _stat(name)
if err != ERROR_NONE {
return 0, err
}
@(require_results)
last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
s := _stat(name) or_return
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE
return File_Time(modified), nil
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -771,11 +793,11 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
if result < 0 {
return s, _get_errno(result)
}
return s, ERROR_NONE
return s, nil
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -785,53 +807,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
if result < 0 {
return s, _get_errno(result)
}
return s, ERROR_NONE
return s, nil
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
@(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
return s, _get_errno(result)
}
return s, ERROR_NONE
return s, nil
}
@private
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
@(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil {
return nil, Errno(get_last_error())
return nil, get_last_error()
}
return dirp, ERROR_NONE
return dirp, nil
}
@private
_closedir :: proc(dirp: Dir) -> Errno {
@(private)
_closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp)
if rc != 0 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
@private
@(private)
_rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp)
}
@private
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
@(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 {
err = Errno(get_last_error())
err = get_last_error()
return
}
err = ERROR_NONE
err = nil
if result == nil {
end_of_stream = true
@@ -842,8 +864,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return
}
@private
_readlink :: proc(path: string) -> (string, Errno) {
@(private, require_results)
_readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -860,12 +882,13 @@ _readlink :: proc(path: string) -> (string, Errno) {
delete(buf)
buf = make([]byte, bufsz)
} 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
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)
}
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
if rel == "" {
rel = "."
@@ -886,25 +910,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
return "", Errno(get_last_error())
return "", get_last_error()
}
defer _unix_free(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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := unix.sys_access(cstr, mask)
if result < 0 {
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) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == 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
}
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
set_env :: proc(key, value: string) -> Errno {
set_env :: proc(key, value: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
key_cstring := strings.clone_to_cstring(key, 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
res := _unix_setenv(key_cstring, value_cstring, 1)
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()
s := strings.clone_to_cstring(key, context.temp_allocator)
res := _unix_putenv(s)
if res < 0 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
@(require_results)
get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems.
@@ -964,14 +991,14 @@ get_current_directory :: proc() -> string {
unreachable()
}
set_current_directory :: proc(path: string) -> (err: Errno) {
set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := unix.sys_chdir(cstr)
if res < 0 {
return _get_errno(res)
}
return ERROR_NONE
return nil
}
exit :: proc "contextless" (code: int) -> ! {
@@ -979,16 +1006,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code))
}
@(require_results)
current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid()
}
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -1004,6 +1034,7 @@ dlerror :: proc() -> string {
return string(_unix_dlerror())
}
@(require_results)
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@@ -1016,11 +1047,12 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
return int(_unix_get_nprocs())
}
@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
@@ -1029,117 +1061,120 @@ _alloc_command_line_arguments :: proc() -> []string {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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))
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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)
if result < 0 {
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))
if result < 0 {
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
File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 /* No error */
EPERM: Errno : 1 /* Operation not permitted */
ENOENT: Errno : 2 /* No such file or directory */
EINTR: Errno : 4 /* Interrupted system call */
ESRCH: Errno : 3 /* No such process */
EIO: Errno : 5 /* Input/output error */
ENXIO: Errno : 6 /* Device not configured */
E2BIG: Errno : 7 /* Argument list too long */
ENOEXEC: Errno : 8 /* Exec format error */
EBADF: Errno : 9 /* Bad file descriptor */
ECHILD: Errno : 10 /* No child processes */
EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */
ENOMEM: Errno : 12 /* Cannot allocate memory */
EACCES: Errno : 13 /* Permission denied */
EFAULT: Errno : 14 /* Bad address */
ENOTBLK: Errno : 15 /* Block device required */
EBUSY: Errno : 16 /* Device busy */
EEXIST: Errno : 17 /* File exists */
EXDEV: Errno : 18 /* Cross-device link */
ENODEV: Errno : 19 /* Operation not supported by device */
ENOTDIR: Errno : 20 /* Not a directory */
EISDIR: Errno : 21 /* Is a directory */
EINVAL: Errno : 22 /* Invalid argument */
ENFILE: Errno : 23 /* Too many open files in system */
EMFILE: Errno : 24 /* Too many open files */
ENOTTY: Errno : 25 /* Inappropriate ioctl for device */
ETXTBSY: Errno : 26 /* Text file busy */
EFBIG: Errno : 27 /* File too large */
ENOSPC: Errno : 28 /* No space left on device */
ESPIPE: Errno : 29 /* Illegal seek */
EROFS: Errno : 30 /* Read-only file system */
EMLINK: Errno : 31 /* Too many links */
EPIPE: Errno : 32 /* Broken pipe */
_Platform_Error :: enum i32 {
NONE = 0,
EPERM = 1, /* Operation not permitted */
ENOENT = 2, /* No such file or directory */
EINTR = 4, /* Interrupted system call */
ESRCH = 3, /* No such process */
EIO = 5, /* Input/output error */
ENXIO = 6, /* Device not configured */
E2BIG = 7, /* Argument list too long */
ENOEXEC = 8, /* Exec format error */
EBADF = 9, /* Bad file descriptor */
ECHILD = 10, /* No child processes */
EDEADLK = 11, /* Resource deadlock avoided. 11 was EAGAIN */
ENOMEM = 12, /* Cannot allocate memory */
EACCES = 13, /* Permission denied */
EFAULT = 14, /* Bad address */
ENOTBLK = 15, /* Block device required */
EBUSY = 16, /* Device busy */
EEXIST = 17, /* File exists */
EXDEV = 18, /* Cross-device link */
ENODEV = 19, /* Operation not supported by device */
ENOTDIR = 20, /* Not a directory */
EISDIR = 21, /* Is a directory */
EINVAL = 22, /* Invalid argument */
ENFILE = 23, /* Too many open files in system */
EMFILE = 24, /* Too many open files */
ENOTTY = 25, /* Inappropriate ioctl for device */
ETXTBSY = 26, /* Text file busy */
EFBIG = 27, /* File too large */
ENOSPC = 28, /* No space left on device */
ESPIPE = 29, /* Illegal seek */
EROFS = 30, /* Read-only file system */
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 */
EDOM: Errno : 33 /* Numerical argument out of domain */
ERANGE: Errno : 34 /* Result too large or too small */
EDOM :: Platform_Error.EDOM /* Numerical argument out of domain */
ERANGE :: Platform_Error.ERANGE /* Result too large or too small */
/* non-blocking and interrupt i/o */
EAGAIN: Errno : 35 /* Resource temporarily unavailable */
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */
EINPROGRESS: Errno : 36 /* Operation now in progress */
EALREADY: Errno : 37 /* Operation already in progress */
EAGAIN :: Platform_Error.EAGAIN /* Resource temporarily unavailable */
EWOULDBLOCK :: EAGAIN /* Operation would block */
EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
/* ipc/network software -- argument errors */
ENOTSOCK: Errno : 38 /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 39 /* Destination address required */
EMSGSIZE: Errno : 40 /* Message too long */
EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 42 /* Protocol option not available */
EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */
ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */
EOPNOTSUPP: Errno : 45 /* Operation not supported */
EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */
EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */
EADDRINUSE: Errno : 48 /* Address already in use */
EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */
ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol option not available */
EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported */
EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol family */
EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Can't assign requested address */
/* ipc/network software -- operational errors */
ENETDOWN: Errno : 50 /* Network is down */
ENETUNREACH: Errno : 51 /* Network is unreachable */
ENETRESET: Errno : 52 /* Network dropped connection on reset */
ECONNABORTED: Errno : 53 /* Software caused connection abort */
ECONNRESET: Errno : 54 /* Connection reset by peer */
ENOBUFS: Errno : 55 /* No buffer space available */
EISCONN: Errno : 56 /* Socket is already connected */
ENOTCONN: Errno : 57 /* Socket is not connected */
ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */
ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */
ETIMEDOUT: Errno : 60 /* Operation timed out */
ECONNREFUSED: Errno : 61 /* Connection refused */
ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection on reset */
ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
EISCONN :: Platform_Error.EISCONN /* Socket is already connected */
ENOTCONN :: Platform_Error.ENOTCONN /* Socket is not connected */
ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Can't send after socket shutdown */
ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: can't splice */
ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Operation timed out */
ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
ELOOP: Errno : 62 /* Too many levels of symbolic links */
ENAMETOOLONG: Errno : 63 /* File name too long */
ELOOP :: Platform_Error.ELOOP /* Too many levels of symbolic links */
ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
/* should be rearranged */
EHOSTDOWN: Errno : 64 /* Host is down */
EHOSTUNREACH: Errno : 65 /* No route to host */
ENOTEMPTY: Errno : 66 /* Directory not empty */
EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
/* quotas & mush */
EPROCLIM: Errno : 67 /* Too many processes */
EUSERS: Errno : 68 /* Too many users */
EDQUOT: Errno : 69 /* Disc quota exceeded */
EPROCLIM :: Platform_Error.EPROCLIM /* Too many processes */
EUSERS :: Platform_Error.EUSERS /* Too many users */
EDQUOT :: Platform_Error.EDQUOT /* Disc quota exceeded */
/* Network File System */
ESTALE: Errno : 70 /* Stale NFS file handle */
EREMOTE: Errno : 71 /* Too many levels of remote in path */
EBADRPC: Errno : 72 /* RPC struct is bad */
ERPCMISMATCH: Errno : 73 /* RPC version wrong */
EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */
EPROGMISMATCH: Errno : 75 /* Program version wrong */
EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */
ESTALE :: Platform_Error.ESTALE /* Stale NFS file handle */
EREMOTE :: Platform_Error.EREMOTE /* Too many levels of remote in path */
EBADRPC :: Platform_Error.EBADRPC /* RPC struct is bad */
ERPCMISMATCH :: Platform_Error.ERPCMISMATCH /* RPC version wrong */
EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL /* RPC prog. not avail */
EPROGMISMATCH :: Platform_Error.EPROGMISMATCH /* Program version wrong */
EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL /* Bad procedure for program */
ENOLCK: Errno : 77 /* No locks available */
ENOSYS: Errno : 78 /* Function not implemented */
ENOLCK :: Platform_Error.ENOLCK /* No locks available */
ENOSYS :: Platform_Error.ENOSYS /* Function not implemented */
EFTYPE: Errno : 79 /* Inappropriate file type or format */
EAUTH: Errno : 80 /* Authentication error */
ENEEDAUTH: Errno : 81 /* Need authenticator */
EFTYPE :: Platform_Error.EFTYPE /* Inappropriate file type or format */
EAUTH :: Platform_Error.EAUTH /* Authentication error */
ENEEDAUTH :: Platform_Error.ENEEDAUTH /* Need authenticator */
/* SystemV IPC */
EIDRM: Errno : 82 /* Identifier removed */
ENOMSG: Errno : 83 /* No message of desired type */
EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */
EIDRM :: Platform_Error.EIDRM /* Identifier removed */
ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large to be stored in data type */
/* 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 */
/* Base, Realtime, Threads or Thread Priority Scheduling option errors */
ENOTSUP: Errno : 86 /* Not supported */
ENOTSUP :: Platform_Error.ENOTSUP /* Not supported */
/* Realtime option errors */
ECANCELED: Errno : 87 /* Operation canceled */
ECANCELED :: Platform_Error.ECANCELED /* Operation canceled */
/* Realtime, XSI STREAMS option errors */
EBADMSG: Errno : 88 /* Bad or Corrupt message */
EBADMSG :: Platform_Error.EBADMSG /* Bad or Corrupt message */
/* XSI STREAMS option errors */
ENODATA: Errno : 89 /* No message available */
ENOSR: Errno : 90 /* No STREAM resources */
ENOSTR: Errno : 91 /* Not a STREAM */
ETIME: Errno : 92 /* STREAM ioctl timeout */
ENODATA :: Platform_Error.ENODATA /* No message available */
ENOSR :: Platform_Error.ENOSR /* No STREAM resources */
ENOSTR :: Platform_Error.ENOSTR /* Not a STREAM */
ETIME :: Platform_Error.ETIME /* STREAM ioctl timeout */
/* File system extended attribute errors */
ENOATTR: Errno : 93 /* Attribute not found */
ENOATTR :: Platform_Error.ENOATTR /* Attribute not found */
/* Realtime, XSI STREAMS option errors */
EMULTIHOP: Errno : 94 /* Multihop attempted */
ENOLINK: Errno : 95 /* Link has been severed */
EPROTO: Errno : 96 /* Protocol error */
EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
EPROTO :: Platform_Error.EPROTO /* Protocol error */
/* Robust mutexes */
EOWNERDEAD: Errno : 97 /* Previous owner died */
ENOTRECOVERABLE: Errno : 98 /* State not recoverable */
EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Previous owner died */
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_WRONLY :: 0x000000001
@@ -268,13 +407,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete
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 }
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 }
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 }
S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
@(require_results) S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
@(require_results) S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
@(require_results) S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
@(require_results) S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
@(require_results) S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
@(require_results) S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
@(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
X_OK :: 1 // Test for execute permission
@@ -306,7 +445,7 @@ foreign libc {
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(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="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.
@(require_results)
is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc "contextless" () -> int {
return int(__errno_location()^)
@(require_results, no_instrumentation)
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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
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)
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.
@(private)
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)
bytes_read := _unix_read(fd, &data[0], to_read)
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 {
return 0, ERROR_NONE
return 0, nil
}
to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write)
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))
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) {
s, err := _fstat(fd)
if err != ERROR_NONE {
return -1, err
}
return s.size, ERROR_NONE
@(require_results)
file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
size = -1
s := _fstat(fd) or_return
size = s.size
return
}
rename :: proc(old_path, new_path: string) -> Errno {
rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_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)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
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 {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISDIR(s.mode)
}
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
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_dir :: proc {is_dir_path, is_dir_handle}
@(require_results)
exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator)
@@ -497,12 +669,13 @@ exists :: proc(path: string) -> bool {
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))
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
@@ -511,38 +684,34 @@ stdin: Handle = 0
stdout: Handle = 1
stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
s, err := _fstat(fd)
if err != ERROR_NONE {
return 0, err
}
@(require_results)
last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
s := _fstat(fd) or_return
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) {
s, err := _stat(name)
if err != ERROR_NONE {
return 0, err
}
@(require_results)
last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
s := _stat(name) or_return
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE
return File_Time(modified), nil
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
if result == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -550,54 +719,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = ---
res := _unix_lstat(cstr, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
@(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = ---
result := _unix_fstat(fd, &s)
if result == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
@(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil {
return nil, Errno(get_last_error())
return nil, get_last_error()
}
return dirp, ERROR_NONE
return dirp, nil
}
@private
_closedir :: proc(dirp: Dir) -> Errno {
@(private)
_closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp)
if rc != 0 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
@private
@(private)
_rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp)
}
@private
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
@(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 {
err = Errno(get_last_error())
err = get_last_error()
return
}
err = ERROR_NONE
err = nil
if result == nil {
end_of_stream = true
@@ -607,8 +776,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return
}
@private
_readlink :: proc(path: string) -> (string, Errno) {
@(private, require_results)
_readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
if rc == -1 {
delete(buf)
return "", Errno(get_last_error())
return "", get_last_error()
} else if rc == int(bufsz) {
bufsz += MAX_PATH
delete(buf)
buf = make([]byte, bufsz)
} 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
_, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0])))
if err != ERROR_NONE {
return "", err
}
path := strings.clone_from_cstring(cstring(&buf[0]))
return path, err
_ = fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) or_return
return strings.clone_from_cstring(cstring(&buf[0]))
}
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
if rel == "" {
rel = "."
@@ -654,26 +820,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
return "", Errno(get_last_error())
return "", get_last_error()
}
defer _unix_free(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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask))
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) {
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
}
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems.
@@ -701,7 +870,7 @@ get_current_directory :: proc() -> string {
if cwd != nil {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
if get_last_error() != ERANGE {
delete(buf)
return ""
}
@@ -710,14 +879,14 @@ get_current_directory :: proc() -> string {
unreachable()
}
set_current_directory :: proc(path: string) -> (err: Errno) {
set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
exit :: proc "contextless" (code: int) -> ! {
@@ -725,10 +894,12 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code))
}
@(require_results)
current_thread_id :: proc "contextless" () -> int {
return int(_lwp_self())
}
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
@@ -736,6 +907,7 @@ dlopen :: proc(filename: string, flags: int) -> rawptr {
return handle
}
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -749,10 +921,12 @@ dlclose :: proc(handle: rawptr) -> bool {
return _unix_dlclose(handle) == 0
}
@(require_results)
dlerror :: proc() -> string {
return string(_unix_dlerror())
}
@(require_results)
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@@ -765,7 +939,7 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
count : int = 0
count_size := size_of(count)
@@ -778,6 +952,7 @@ _processor_core_count :: proc() -> int {
return 1
}
@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
+341 -204
View File
@@ -9,108 +9,205 @@ import "base:runtime"
Handle :: distinct i32
Pid :: distinct i32
File_Time :: distinct u64
Errno :: distinct i32
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
ENOENT: Errno: 2
ESRCH: Errno: 3
EINTR: Errno: 4
EIO: Errno: 5
ENXIO: Errno: 6
E2BIG: Errno: 7
ENOEXEC: Errno: 8
EBADF: Errno: 9
ECHILD: Errno: 10
EDEADLK: Errno: 11
ENOMEM: Errno: 12
EACCES: Errno: 13
EFAULT: Errno: 14
ENOTBLK: Errno: 15
EBUSY: Errno: 16
EEXIST: Errno: 17
EXDEV: Errno: 18
ENODEV: Errno: 19
ENOTDIR: Errno: 20
EISDIR: Errno: 21
EINVAL: Errno: 22
ENFILE: Errno: 23
EMFILE: Errno: 24
ENOTTY: Errno: 25
ETXTBSY: Errno: 26
EFBIG: Errno: 27
ENOSPC: Errno: 28
ESPIPE: Errno: 29
EROFS: Errno: 30
EMLINK: Errno: 31
EPIPE: Errno: 32
EDOM: Errno: 33
ERANGE: Errno: 34
EAGAIN: Errno: 35
EWOULDBLOCK: Errno: EAGAIN
EINPROGRESS: Errno: 36
EALREADY: Errno: 37
ENOTSOCK: Errno: 38
EDESTADDRREQ: Errno: 39
EMSGSIZE: Errno: 40
EPROTOTYPE: Errno: 41
ENOPROTOOPT: Errno: 42
EPROTONOSUPPORT: Errno: 43
ESOCKTNOSUPPORT: Errno: 44
EOPNOTSUPP: Errno: 45
EPFNOSUPPORT: Errno: 46
EAFNOSUPPORT: Errno: 47
EADDRINUSE: Errno: 48
EADDRNOTAVAIL: Errno: 49
ENETDOWN: Errno: 50
ENETUNREACH: Errno: 51
ENETRESET: Errno: 52
ECONNABORTED: Errno: 53
ECONNRESET: Errno: 54
ENOBUFS: Errno: 55
EISCONN: Errno: 56
ENOTCONN: Errno: 57
ESHUTDOWN: Errno: 58
ETOOMANYREFS: Errno: 59
ETIMEDOUT: Errno: 60
ECONNREFUSED: Errno: 61
ELOOP: Errno: 62
ENAMETOOLONG: Errno: 63
EHOSTDOWN: Errno: 64
EHOSTUNREACH: Errno: 65
ENOTEMPTY: Errno: 66
EPROCLIM: Errno: 67
EUSERS: Errno: 68
EDQUOT: Errno: 69
ESTALE: Errno: 70
EREMOTE: Errno: 71
EBADRPC: Errno: 72
ERPCMISMATCH: Errno: 73
EPROGUNAVAIL: Errno: 74
EPROGMISMATCH: Errno: 75
EPROCUNAVAIL: Errno: 76
ENOLCK: Errno: 77
ENOSYS: Errno: 78
EFTYPE: Errno: 79
EAUTH: Errno: 80
ENEEDAUTH: Errno: 81
EIPSEC: Errno: 82
ENOATTR: Errno: 83
EILSEQ: Errno: 84
ENOMEDIUM: Errno: 85
EMEDIUMTYPE: Errno: 86
EOVERFLOW: Errno: 87
ECANCELED: Errno: 88
EIDRM: Errno: 89
ENOMSG: Errno: 90
ENOTSUP: Errno: 91
EBADMSG: Errno: 92
ENOTRECOVERABLE: Errno: 93
EOWNERDEAD: Errno: 94
EPROTO: Errno: 95
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
EDEADLK :: Platform_Error.EDEADLK
ENOMEM :: Platform_Error.ENOMEM
EACCES :: Platform_Error.EACCES
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
EWOULDBLOCK :: Platform_Error.EWOULDBLOCK
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
ETOOMANYREFS :: Platform_Error.ETOOMANYREFS
ETIMEDOUT :: Platform_Error.ETIMEDOUT
ECONNREFUSED :: Platform_Error.ECONNREFUSED
ELOOP :: Platform_Error.ELOOP
ENAMETOOLONG :: Platform_Error.ENAMETOOLONG
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
EREMOTE :: Platform_Error.EREMOTE
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
EIPSEC :: Platform_Error.EIPSEC
ENOATTR :: Platform_Error.ENOATTR
EILSEQ :: Platform_Error.EILSEQ
ENOMEDIUM :: Platform_Error.ENOMEDIUM
EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE
EOVERFLOW :: Platform_Error.EOVERFLOW
ECANCELED :: Platform_Error.ECANCELED
EIDRM :: Platform_Error.EIDRM
ENOMSG :: Platform_Error.ENOMSG
ENOTSUP :: Platform_Error.ENOTSUP
EBADMSG :: Platform_Error.EBADMSG
ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE
EOWNERDEAD :: Platform_Error.EOWNERDEAD
EPROTO :: Platform_Error.EPROTO
O_RDONLY :: 0x00000
O_WRONLY :: 0x00001
@@ -225,13 +322,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution
S_ISTXT :: 0o1000 // Sticky bit
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 }
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 }
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 }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
@(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
@(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
@(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
@(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
@(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
@(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
@(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0x00 // Test for file existance
X_OK :: 0x01 // Test for execute permission
@@ -291,38 +388,47 @@ foreign libc {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
@(require_results)
is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc "contextless" () -> int {
return int(__error()^)
@(require_results, no_instrumentation)
get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__error()^)
}
fork :: proc() -> (Pid, Errno) {
@(require_results)
fork :: proc() -> (Pid, Error) {
pid := _unix_fork()
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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
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)
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`.
@@ -333,124 +439,148 @@ close :: proc(fd: Handle) -> Errno {
@(private)
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)
bytes_read := _unix_read(fd, &data[0], to_read)
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 {
return 0, ERROR_NONE
return 0, nil
}
to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write)
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))
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) {
s, err := _fstat(fd)
if err != ERROR_NONE {
return -1, err
}
return s.size, ERROR_NONE
@(require_results)
file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
size = -1
s := _fstat(fd) or_return
size = s.size
return
}
rename :: proc(old_path, new_path: string) -> Errno {
rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_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)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
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()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
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 {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISREG(s.mode)
}
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd)
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISDIR(s.mode)
}
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat
err: Errno
err: Error
if follow_links {
s, err = _stat(path)
} else {
s, err = _lstat(path)
}
if err != ERROR_NONE {
if err != nil {
return false
}
return S_ISDIR(s.mode)
@@ -469,26 +599,22 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
s, err := _fstat(fd)
if err != ERROR_NONE {
return 0, err
}
@(require_results)
last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
s := _fstat(fd) or_return
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) {
s, err := _stat(name)
if err != ERROR_NONE {
return 0, err
}
@(require_results)
last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
s := _stat(name) or_return
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE
return File_Time(modified), nil
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -496,13 +622,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = ---
res := _unix_stat(cstr, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
@(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -510,55 +636,55 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = ---
res := _unix_lstat(cstr, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
@(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized
s: OS_Stat = ---
res := _unix_fstat(fd, &s)
if res == -1 {
return s, Errno(get_last_error())
return s, get_last_error()
}
return s, ERROR_NONE
return s, nil
}
@private
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
@(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil {
return nil, Errno(get_last_error())
return nil, get_last_error()
}
return dirp, ERROR_NONE
return dirp, nil
}
@private
_closedir :: proc(dirp: Dir) -> Errno {
@(private)
_closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp)
if rc != 0 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
@private
@(private)
_rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp)
}
@private
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
@(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 {
err = Errno(get_last_error())
err = get_last_error()
return
}
err = ERROR_NONE
err = nil
if result == nil {
end_of_stream = true
@@ -568,8 +694,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return
}
@private
_readlink :: proc(path: string) -> (string, Errno) {
@(private, require_results)
_readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
if rc == -1 {
delete(buf)
return "", Errno(get_last_error())
return "", get_last_error()
} else if rc == int(bufsz) {
bufsz += MAX_PATH
delete(buf)
buf = make([]byte, bufsz)
} else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
return strings.string_from_ptr(&buf[0], rc), nil
}
}
}
// XXX OpenBSD
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
return "", Errno(ENOSYS)
@(require_results)
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
if rel == "" {
rel = "."
@@ -606,25 +734,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
return "", Errno(get_last_error())
return "", get_last_error()
}
defer _unix_free(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()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask))
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) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == 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
}
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
get_current_directory :: proc() -> string {
buf := make([dynamic]u8, MAX_PATH)
for {
@@ -647,7 +778,7 @@ get_current_directory :: proc() -> string {
if cwd != nil {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
if get_last_error() != ERANGE {
delete(buf)
return ""
}
@@ -656,14 +787,14 @@ get_current_directory :: proc() -> string {
unreachable()
}
set_current_directory :: proc(path: string) -> (err: Errno) {
set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
return Errno(get_last_error())
return get_last_error()
}
return ERROR_NONE
return nil
}
exit :: proc "contextless" (code: int) -> ! {
@@ -671,16 +802,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code))
}
@(require_results)
current_thread_id :: proc "contextless" () -> int {
return _unix_getthrid()
}
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -692,10 +826,12 @@ dlclose :: proc(handle: rawptr) -> bool {
assert(handle != nil)
return _unix_dlclose(handle) == 0
}
@(require_results)
dlerror :: proc() -> string {
return string(_unix_dlerror())
}
@(require_results)
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@@ -710,11 +846,12 @@ get_page_size :: proc() -> int {
_SC_NPROCESSORS_ONLN :: 503
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
return int(_sysconf(_SC_NPROCESSORS_ONLN))
}
@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
+25 -18
View File
@@ -4,12 +4,10 @@ import "core:sys/wasm/wasi"
import "base:runtime"
Handle :: distinct i32
Errno :: distinct i32
_Platform_Error :: wasi.errno_t
INVALID_HANDLE :: -1
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
O_RDONLY :: 0x00000
O_WRONLY :: 0x00001
O_RDWR :: 0x00002
@@ -29,6 +27,7 @@ stderr: Handle = 2
args := _alloc_command_line_arguments()
@(require_results)
_alloc_command_line_arguments :: proc() -> (args: []string) {
args = make([]string, len(runtime.args__))
for &arg, i in args {
@@ -93,8 +92,9 @@ init_preopens :: proc() {
preopens = dyn_preopens[:]
}
@(require_results)
wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
@(require_results)
prefix_matches :: proc(prefix, path: string) -> bool {
// Empty is valid for any relative path.
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) {
iovs := wasi.ciovec_t(data)
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) {
iovs := wasi.iovec_t(data)
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) {
iovs := wasi.ciovec_t(data)
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) {
iovs := wasi.iovec_t(data)
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) {
oflags: wasi.oflags_t
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)
return Handle(fd), Errno(err)
return Handle(fd), Platform_Error(err)
}
close :: proc(fd: Handle) -> Errno {
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) {
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 {
return 0
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
return 1
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
if err != nil {
return 0, Errno(err)
}
return i64(stat.size), 0
@(require_results)
file_size :: proc(fd: Handle) -> (size: i64, err: Errno) {
stat := wasi.fd_filestat_get(wasi.fd_t(fd)) or_return
size = i64(stat.size)
return
}
+106 -48
View File
@@ -7,7 +7,6 @@ import "base:intrinsics"
Handle :: distinct uintptr
File_Time :: distinct u64
Errno :: distinct int
INVALID_HANDLE :: ~Handle(0)
@@ -27,70 +26,119 @@ O_SYNC :: 0x01000
O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000
_Platform_Error :: win32.System_Error
ERROR_NONE: Errno : 0
ERROR_FILE_NOT_FOUND: Errno : 2
ERROR_PATH_NOT_FOUND: Errno : 3
ERROR_ACCESS_DENIED: Errno : 5
ERROR_INVALID_HANDLE: Errno : 6
ERROR_NOT_ENOUGH_MEMORY: Errno : 8
ERROR_NO_MORE_FILES: Errno : 18
ERROR_HANDLE_EOF: Errno : 38
ERROR_NETNAME_DELETED: Errno : 64
ERROR_FILE_EXISTS: Errno : 80
ERROR_INVALID_PARAMETER: Errno : 87
ERROR_BROKEN_PIPE: Errno : 109
ERROR_BUFFER_OVERFLOW: Errno : 111
ERROR_INSUFFICIENT_BUFFER: Errno : 122
ERROR_MOD_NOT_FOUND: Errno : 126
ERROR_PROC_NOT_FOUND: Errno : 127
ERROR_DIR_NOT_EMPTY: Errno : 145
ERROR_ALREADY_EXISTS: Errno : 183
ERROR_ENVVAR_NOT_FOUND: Errno : 203
ERROR_MORE_DATA: Errno : 234
ERROR_OPERATION_ABORTED: Errno : 995
ERROR_IO_PENDING: Errno : 997
ERROR_NOT_FOUND: Errno : 1168
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314
WSAEACCES: Errno : 10013
WSAECONNRESET: Errno : 10054
ERROR_FILE_NOT_FOUND :: _Platform_Error(2)
ERROR_PATH_NOT_FOUND :: _Platform_Error(3)
ERROR_ACCESS_DENIED :: _Platform_Error(5)
ERROR_INVALID_HANDLE :: _Platform_Error(6)
ERROR_NOT_ENOUGH_MEMORY :: _Platform_Error(8)
ERROR_NO_MORE_FILES :: _Platform_Error(18)
ERROR_HANDLE_EOF :: _Platform_Error(38)
ERROR_NETNAME_DELETED :: _Platform_Error(64)
ERROR_FILE_EXISTS :: _Platform_Error(80)
ERROR_INVALID_PARAMETER :: _Platform_Error(87)
ERROR_BROKEN_PIPE :: _Platform_Error(109)
ERROR_BUFFER_OVERFLOW :: _Platform_Error(111)
ERROR_INSUFFICIENT_BUFFER :: _Platform_Error(122)
ERROR_MOD_NOT_FOUND :: _Platform_Error(126)
ERROR_PROC_NOT_FOUND :: _Platform_Error(127)
ERROR_DIR_NOT_EMPTY :: _Platform_Error(145)
ERROR_ALREADY_EXISTS :: _Platform_Error(183)
ERROR_ENVVAR_NOT_FOUND :: _Platform_Error(203)
ERROR_MORE_DATA :: _Platform_Error(234)
ERROR_OPERATION_ABORTED :: _Platform_Error(995)
ERROR_IO_PENDING :: _Platform_Error(997)
ERROR_NOT_FOUND :: _Platform_Error(1168)
ERROR_PRIVILEGE_NOT_HELD :: _Platform_Error(1314)
WSAEACCES :: _Platform_Error(10013)
WSAECONNRESET :: _Platform_Error(10054)
// Windows reserves errors >= 1<<29 for application use
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
// "Argv" arguments converted to Odin strings
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)
}
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
@(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
file_info: win32.BY_HANDLE_FILE_INFORMATION
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)
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
wide_path := win32.utf8_to_wstring(name)
if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) {
return 0, Errno(win32.GetLastError())
return 0, get_last_error()
}
l := File_Time(data.ftLastWriteTime.dwLowDateTime)
h := File_Time(data.ftLastWriteTime.dwHighDateTime)
return l | h << 32, ERROR_NONE
return l | h << 32, nil
}
@(require_results)
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@@ -105,7 +153,7 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
@(private, require_results)
_processor_core_count :: proc() -> int {
length : win32.DWORD = 0
result := win32.GetLogicalProcessorInformation(nil, &length)
@@ -136,12 +184,14 @@ exit :: proc "contextless" (code: int) -> ! {
@(require_results)
current_thread_id :: proc "contextless" () -> int {
return int(win32.GetCurrentThreadId())
}
@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
arg_count: i32
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
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW {
@(require_results)
get_windows_version_w :: proc "contextless" () -> win32.OSVERSIONINFOEXW {
osvi : win32.OSVERSIONINFOEXW
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
win32.RtlGetVersion(&osvi)
return osvi
}
is_windows_xp :: proc() -> bool {
@(require_results)
is_windows_xp :: proc "contextless" () -> bool {
osvi := get_windows_version_w()
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()
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()
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()
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()
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()
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()
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 {
return time.Time{
_nsec = uft.nanoseconds + uft.seconds * 1_000_000_000,
}
}
@private
@(private)
_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
fi.size = s.size
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 {
is_separator :: proc(c: byte) -> bool {
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
s: OS_Stat
s, err = _lstat(name)
if err != ERROR_NONE {
return fi, err
}
s := _lstat(name) or_return
_fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_relative(name)
if err != ERROR_NONE {
return
}
fi.fullpath = absolute_path_from_relative(name) or_return
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
s: OS_Stat
s, err = _stat(name)
if err != ERROR_NONE {
return fi, err
}
s := _stat(name) or_return
_fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_relative(name)
if err != ERROR_NONE {
return
}
fi.fullpath = absolute_path_from_relative(name) or_return
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
s: OS_Stat
s, err = _fstat(fd)
if err != ERROR_NONE {
return fi, err
}
s := _fstat(fd) or_return
_fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
fi.fullpath = absolute_path_from_handle(fd) or_return
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 win32 "core:sys/windows"
@(private)
@(private, require_results)
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
context.allocator = allocator
@@ -19,10 +19,10 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
for {
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 {
return "", Errno(win32.GetLastError())
return "", get_last_error()
}
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)
}
@@ -30,7 +30,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
return
}
@(private)
@(private, require_results)
_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
if len(name) == 0 {
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
sh := win32.FindFirstFileW(wname, &fd)
if sh == win32.INVALID_HANDLE_VALUE {
e = Errno(win32.GetLastError())
e = get_last_error()
return
}
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)
if h == win32.INVALID_HANDLE_VALUE {
e = Errno(win32.GetLastError())
e = get_last_error()
return
}
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) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
return _stat(name, attrs, allocator)
}
@(require_results)
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
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 {
return {}, ERROR_INVALID_HANDLE
err = ERROR_INVALID_HANDLE
}
context.allocator = allocator
path, err := cleanpath_from_handle(fd)
if err != ERROR_NONE {
return {}, err
path := cleanpath_from_handle(fd) or_return
defer if err != nil {
delete(path)
}
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:
fi.name = basename(path)
fi.mode |= file_type_mode(h)
errno = ERROR_NONE
err = nil
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
return
}
@(private)
@(private, require_results)
cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
buf := buf
N := 0
@@ -133,16 +136,13 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
return buf
}
@(private)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
@(private, require_results)
cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
buf, err := cleanpath_from_handle_u16(fd, context.temp_allocator)
if err != 0 {
return "", err
}
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
return win32.utf16_to_utf8(buf, context.allocator)
}
@(private)
@(private, require_results)
cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
if fd == 0 {
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)
if n == 0 {
return nil, Errno(win32.GetLastError())
return nil, get_last_error()
}
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
return buf[:buf_len], ERROR_NONE
return buf[:buf_len], nil
}
@(private)
@(private, require_results)
cleanpath_from_buf :: proc(buf: []u16) -> string {
buf := buf
buf = cleanpath_strip_prefix(buf)
return win32.utf16_to_utf8(buf, context.allocator) or_else ""
}
@(private)
@(private, require_results)
basename :: proc(name: string) -> (base: string) {
name := name
if len(name) > 3 && name[:3] == `\\?` {
@@ -190,7 +190,7 @@ basename :: proc(name: string) -> (base: string) {
return name
}
@(private)
@(private, require_results)
file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
switch win32.GetFileType(h) {
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) {
if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
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))
}
@(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) {
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
}
@(private)
@(private, require_results)
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)
@@ -269,20 +269,20 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
return
}
@(private)
@(private, require_results)
file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) {
err := Errno(win32.GetLastError())
err := get_last_error()
return {}, err
}
ti: win32.FILE_ATTRIBUTE_TAG_INFO
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
err := win32.GetLastError()
if err != u32(ERROR_INVALID_PARAMETER) {
return {}, Errno(err)
err := get_last_error()
if err != ERROR_INVALID_PARAMETER {
return {}, err
}
// Indicate this is a symlink on FAT file systems
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)
return fi, ERROR_NONE
return fi, nil
}
+16 -33
View File
@@ -14,44 +14,36 @@ 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) {
fd := Handle(uintptr(stream_data))
n_int: int
os_err: Errno
os_err: Error
switch mode {
case .Close:
close(fd)
os_err = close(fd)
case .Flush:
when ODIN_OS == .Windows {
flush(fd)
} else {
// TOOD(bill): other operating systems
}
os_err = flush(fd)
case .Read:
n_int, os_err = read(fd, p)
n = i64(n_int)
if n == 0 && os_err == 0 {
if n == 0 && os_err == nil {
err = .EOF
}
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 = i64(n_int)
if n == 0 && os_err == 0 {
err = .EOF
}
n_int, os_err = read_at(fd, p, offset)
n = i64(n_int)
if n == 0 && os_err == nil {
err = .EOF
}
case .Write:
n_int, os_err = write(fd, p)
n = i64(n_int)
if n == 0 && os_err == 0 {
if n == 0 && os_err == nil {
err = .EOF
}
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 = i64(n_int)
if n == 0 && os_err == 0 {
err = .EOF
}
n_int, os_err = write_at(fd, p, offset)
n = i64(n_int)
if n == 0 && os_err == nil {
err = .EOF
}
case .Seek:
n, os_err = seek(fd, offset, int(whence))
@@ -60,20 +52,11 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
case .Destroy:
err = .Empty
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 {
when ODIN_OS == .Windows {
if os_err == ERROR_HANDLE_EOF {
return n, .EOF
}
}
err = .Unknown
if err == nil && os_err != nil {
err = error_to_io_error(os_err)
}
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)
if derr != 0 {
if derr != nil {
return
}
defer os.close(d)
@@ -280,7 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info)
if ferr != 0 {
if ferr != nil {
return
}
if !file_info.is_dir {
+5 -5
View File
@@ -52,7 +52,7 @@ is_abs :: proc(path: string) -> bool {
@(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
name := name
@@ -63,17 +63,17 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
p := win32.utf8_to_utf16(name, ta)
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
if n == 0 {
return "", os.Errno(win32.GetLastError())
return "", os.get_last_error()
}
buf := make([]u16, n, ta)
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 {
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) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := temp_full_path(path)
if err != 0 {
if err != nil {
return "", false
}
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:
// 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
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'
// 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: walk does not follow symbolic links
// 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)
defer os.file_info_delete(info, context.temp_allocator)
skip_dir: bool
if err != 0 {
if err != nil {
err, skip_dir = walk_proc(info, err, user_data)
} else {
err, skip_dir = _walk(info, walk_proc, user_data)
}
return 0 if skip_dir else err
return nil if skip_dir else err
}
@(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.fullpath == "" && info.name == "" {
// ignore empty things
return
}
return walk_proc(info, 0, user_data)
return walk_proc(info, nil, user_data)
}
fis: []os.File_Info
err1: os.Errno
err1: os.Error
fis, err = read_dir(info.fullpath, context.temp_allocator)
defer os.file_info_slice_delete(fis, context.temp_allocator)
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
return
}
for fi in fis {
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 {
return
}
@@ -70,19 +70,12 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (e
}
@(private)
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) {
f, err := os.open(dir_name, os.O_RDONLY)
if err != 0 {
return nil, err
}
fis: []os.File_Info
fis, err = os.read_dir(f, -1, allocator)
os.close(f)
if err != 0 {
return nil, err
}
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> (fis: []os.File_Info, err: os.Error) {
f := os.open(dir_name, os.O_RDONLY) or_return
defer os.close(f)
fis = os.read_dir(f, -1, allocator) or_return
slice.sort_by(fis, proc(a, b: os.File_Info) -> bool {
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:sync"
spall_ctx: spall.Context
spall_buffer: spall.Buffer
@(thread_local) spall_buffer: spall.Buffer
foo :: proc() {
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
@@ -13,7 +15,7 @@
defer spall.context_destroy(&spall_ctx)
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)
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 {
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
}
@@ -227,7 +227,7 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch
}
@(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)
}
+3 -12
View File
@@ -10,21 +10,12 @@ import "core:sys/linux"
MAX_RW :: 0x7fffffff
@(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 {
return 0, os.ERROR_NONE
}
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
for n < len(data) {
chunk := data[:min(len(data), MAX_RW)]
written, errno := linux.write(linux.Fd(fd), chunk)
if errno != .NONE {
return n, os.Errno(errno)
}
n += written
n += linux.write(linux.Fd(fd), chunk) or_return
}
return n, os.ERROR_NONE
return
}
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 ---
}
@(no_instrumentation)
get_last_error :: proc "contextless" () -> int {
return int(__error()^)
}
MAX_RW :: 0x7fffffff
@(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
return 0, nil
}
for n < len(data) {
chunk := data[:min(len(data), MAX_RW)]
written := _unix_write(fd, raw_data(chunk), len(chunk))
if written < 0 {
return n, os.Errno(get_last_error())
return n, os.get_last_error()
}
n += written
}
return n, os.ERROR_NONE
return n, nil
}
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
@(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 {
return 0, os.ERROR_NONE
return 0, nil
}
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)
if single_write_length <= 0 || !e {
err := os.Errno(win32.GetLastError())
return int(total_write), err
return int(total_write), os.get_last_error()
}
total_write += i64(single_write_length)
}
return int(total_write), os.ERROR_NONE
return int(total_write), nil
}
@(no_instrumentation)
+58
View File
@@ -496,6 +496,64 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
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)
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
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
// Saturation Arithmetic
add_sat :: intrinsics.simd_add_sat
sub_sat :: intrinsics.simd_sub_sat
saturating_add :: intrinsics.simd_saturating_add
saturating_sub :: intrinsics.simd_saturating_sub
bit_and :: intrinsics.simd_bit_and
bit_or :: intrinsics.simd_bit_or
@@ -102,6 +102,15 @@ lanes_le :: intrinsics.simd_lanes_le
lanes_gt :: intrinsics.simd_lanes_gt
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 :: intrinsics.simd_extract
// 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_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 :: 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")
_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")
_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")
_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")
_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")
_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")
_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")
_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")
_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")
_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)
}
+440 -32
View File
@@ -2,44 +2,452 @@ package sync
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
/*
Atomic_Memory_Order :: enum {
Relaxed = 0, // Unordered
Consume = 1, // Monotonic
Acquire = 2,
Release = 3,
Acq_Rel = 4,
Seq_Cst = 5,
}
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
Consume = 1, // Monotonic
Acquire = 2,
Release = 3,
Acq_Rel = 4,
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
/*
Establish memory ordering.
atomic_thread_fence :: intrinsics.atomic_thread_fence
atomic_signal_fence :: intrinsics.atomic_signal_fence
atomic_store :: intrinsics.atomic_store
atomic_store_explicit :: intrinsics.atomic_store_explicit
atomic_load :: intrinsics.atomic_load
atomic_load_explicit :: intrinsics.atomic_load_explicit
atomic_add :: intrinsics.atomic_add
atomic_add_explicit :: intrinsics.atomic_add_explicit
atomic_sub :: intrinsics.atomic_sub
atomic_sub_explicit :: intrinsics.atomic_sub_explicit
atomic_and :: intrinsics.atomic_and
atomic_and_explicit :: intrinsics.atomic_and_explicit
atomic_nand :: intrinsics.atomic_nand
atomic_nand_explicit :: intrinsics.atomic_nand_explicit
atomic_or :: intrinsics.atomic_or
atomic_or_explicit :: intrinsics.atomic_or_explicit
atomic_xor :: intrinsics.atomic_xor
atomic_xor_explicit :: intrinsics.atomic_xor_explicit
atomic_exchange :: intrinsics.atomic_exchange
atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
This procedure establishes memory ordering, without an associated atomic
operation.
*/
atomic_thread_fence :: intrinsics.atomic_thread_fence
// Returns value and optional ok boolean
atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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
/*
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_weak :: intrinsics.atomic_compare_exchange_weak
atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_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
/*
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
+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
+384 -85
View File
@@ -4,15 +4,41 @@ import "core:time"
import vg "core:sys/valgrind"
_ :: vg
// A Wait_Group waits for a collection of threads to finish
//
// A Wait_Group must not be copied after first use
/*
Wait group.
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 {
counter: int,
mutex: Mutex,
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) {
if delta == 0 {
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_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) {
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 {
if duration <= 0 {
return false
@@ -64,41 +111,43 @@ wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: t
return true
}
/*
A barrier enabling multiple threads to synchronize the beginning of some computation
Barrier.
Example:
package example
A barrier is a synchronization primitive enabling multiple threads to
synchronize the beginning of some computation.
import "core:fmt"
import "core:sync"
import "core:thread"
When `barrier_wait` procedure is called by any thread, that thread will block
the execution, until all threads associated with the barrier reach the same
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" () {
fmt.println("Start")
This type of synchronization primitive can be used to synchronize "staged"
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.
THREAD_COUNT :: 4
threads: [THREAD_COUNT]^thread.Thread
**Example**:
sync.barrier_init(barrier, THREAD_COUNT)
for _, i in threads {
threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
// Same messages will be printed together but without any interleaving
fmt.println("Getting ready!")
sync.barrier_wait(barrier)
fmt.println("Off their marks they go!")
})
}
for t in threads {
thread.destroy(t) // join and free thread
}
fmt.println("Finished")
THREAD_COUNT :: 4
threads: [THREAD_COUNT]^thread.Thread
sync.barrier_init(barrier, THREAD_COUNT)
for _, i in threads {
threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
// Same messages will be printed together but without any interleaving
fmt.println("Getting ready!")
sync.barrier_wait(barrier)
fmt.println("Off their marks they go!")
})
}
for t in threads {
thread.destroy(t)
}
*/
Barrier :: struct #no_copy {
@@ -109,6 +158,13 @@ Barrier :: struct #no_copy {
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) {
when ODIN_VALGRIND_SUPPORT {
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
}
// 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) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_barrier_wait_pre(b)
@@ -140,15 +201,31 @@ barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
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 {
// 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: i32,
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) {
old_status := atomic_load_explicit(&e.status, .Relaxed)
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) {
old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
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: 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 := atomic_add_explicit(&m.ticket, 1, .Relaxed)
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) {
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)
ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {
ticket_mutex_lock(m)
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 {
counter: i32,
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) {
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
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 {
v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
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) {
if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
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)
benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool {
benaphore_lock(m)
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 {
counter: int,
owner: int,
@@ -229,6 +437,16 @@ Recursive_Benaphore :: struct #no_copy {
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) {
tid := current_thread_id()
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
@@ -241,6 +459,17 @@ recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {
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 {
tid := current_thread_id()
if b.owner == tid {
@@ -256,6 +485,13 @@ recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) ->
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) {
tid := current_thread_id()
_assert(tid == b.owner, "tid != b.owner")
@@ -272,24 +508,50 @@ recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {
// 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)
recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool {
recursive_benaphore_lock(m)
return true
}
/*
Once action.
// Once is a data value that will perform exactly on action.
//
// A Once must not be copied after first use.
`Once` a synchronization primitive, that only allows a single entry into a
critical section from a single thread.
*/
Once :: struct #no_copy {
m: Mutex,
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_without_data,
once_do_without_data_contextless,
@@ -297,7 +559,9 @@ once_do :: proc{
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()) {
@(cold)
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" ()) {
@(cold)
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) {
@(cold)
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) {
@(cold)
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:
// 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
// blocks for the specified duration.
// * The `unpark` procedure automatically makes the token available if it
// was not already.
* 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
blocks for the specified duration.
* The `unpark` procedure automatically makes the token available if it
was not already.
*/
Parker :: struct #no_copy {
state: Futex,
}
// Blocks the current thread until the token is made available.
//
// Assumes this is only called by the thread that owns the Parker.
@(private="file") PARKER_EMPTY :: 0
@(private="file") PARKER_NOTIFIED :: 1
@(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) {
EMPTY :: 0
NOTIFIED :: 1
PARKED :: max(u32)
if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
if atomic_sub_explicit(&p.state, 1, .Acquire) == PARKER_NOTIFIED {
return
}
for {
futex_wait(&p.state, PARKED)
if _, ok := atomic_compare_exchange_strong_explicit(&p.state, NOTIFIED, EMPTY, .Acquire, .Acquire); ok {
futex_wait(&p.state, PARKER_PARKED)
if _, ok := atomic_compare_exchange_strong_explicit(&p.state, PARKER_NOTIFIED, PARKER_EMPTY, .Acquire, .Acquire); ok {
return
}
}
}
// Blocks the current thread until the token is made available, but only
// for a limited duration.
//
// Assumes this is only called by the thread that owns the Parker
/*
Blocks until the token is available with timeout.
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) {
EMPTY :: 0
NOTIFIED :: 1
PARKED :: max(u32)
if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
start_tick := time.tick_now()
remaining_duration := duration
if atomic_sub_explicit(&p.state, 1, .Acquire) == PARKER_NOTIFIED {
return
}
futex_wait_with_timeout(&p.state, PARKED, duration)
atomic_exchange_explicit(&p.state, EMPTY, .Acquire)
for {
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) {
EMPTY :: 0
NOTIFIED :: 1
PARKED :: max(Futex)
if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED {
if atomic_exchange_explicit((^u32)(&p.state), PARKER_NOTIFIED, .Release) == PARKER_PARKED {
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
// is made available
// * The `one_shot_event_signal` procedure automatically makes the token
// available if its was not already.
* The `one_shot_event_wait` blocks the current thread until the event
is made available
* The `one_shot_event_signal` procedure automatically makes the token
available if its was not already.
*/
One_Shot_Event :: struct #no_copy {
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) {
for atomic_load_explicit(&e.state, .Acquire) == 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) {
atomic_store_explicit(&e.state, 1, .Release)
futex_broadcast(&e.state)
+5 -6
View File
@@ -2,7 +2,6 @@
package sync
import "core:c"
import "base:runtime"
import "core:sys/haiku"
import "core:sys/unix"
import "core:time"
@@ -86,10 +85,10 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
waiter.prev.next = waiter.next
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!
return
return
}
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) {
@@ -133,10 +132,10 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration
waiter.prev.next = waiter.next
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!
return
// FIXME: Add error handling!
return
}
_futex_signal :: proc "contextless" (f: ^Futex) {
+417 -53
View File
@@ -3,46 +3,108 @@ package sync
import "base:runtime"
import "core:time"
/*
Obtain the current thread ID.
*/
current_thread_id :: proc "contextless" () -> int {
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,
// and thus prevent access to same piece of memory by multiple threads, at the same time.
//
// A Mutex's zero value represents an initial, *unlocked* state.
//
// If another thread tries to take the lock while another thread holds it, it will pause
// until the lock is released. Code or memory that is "surrounded" by a mutex lock is said
// to be "guarded by a mutex".
//
// 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`.
/*
Mutual exclusion lock.
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 entering the critical
section, and thus prevent access to same piece of memory by multiple threads, at
the same time.
Mutex's zero-initializzed value represents an initial, *unlocked* state.
If another thread tries to acquire the lock, while it's already held (typically
by another thread), the thread's execution will be blocked, until the lock is
released. Code or memory that is "surrounded" by a mutex lock and unlock
operations is said to be "guarded by a mutex".
**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 {
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(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(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 {
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) {
...
}
@@ -53,47 +115,145 @@ mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
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
// The zero value for a RW_Mutex is an unlocked mutex
//
// A RW_Mutex must not be copied after first use
/*
Read-write mutual exclusion lock.
An `RW_Mutex` is a reader/writer mutual exclusion lock. The lock can be held by
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 {
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(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(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 {
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(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(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 {
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) {
...
}
@@ -105,8 +265,23 @@ rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
}
/*
Example:
if rw_mutex_shared_guard(&m) {
Guard the current scope with a shared lock on a read-write mutex.
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
}
/*
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
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// A Recursive_Mutex must not be copied after first use
When a lock is acquired on a recursive mutex, all other threads attempting to
acquire a lock on the same mutex will be blocked from any critical sections,
associated with the same recrusive mutex.
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 {
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(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(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 {
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) {
...
}
@@ -150,19 +386,69 @@ recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
return true
}
/*
A condition variable.
// Cond implements a condition variable, a rendezvous point for threads
// waiting for signalling the occurence of an event
//
// A Cond must not be copied after first use
`Cond` implements a condition variable, a rendezvous point for threads waiting
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
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 {
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(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 {
if duration <= 0 {
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)
}
/*
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(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(c)
}
/*
Semaphore.
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
// Posting to the semaphore increases the count by one, or the provided amount.
//
// A Sema must not be copied after first use
When waited upon, semaphore blocks until the internal count is greater than
zero, then decrements the internal counter by one. Posting to the semaphore
increases the count by one, or the provided amount.
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 {
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(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(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 {
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
//
// An Futex must not be copied after first use
**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
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
/*
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) {
if u32(atomic_load_explicit(f, .Acquire)) != expected {
return
}
_assert(_futex_wait(f, expected), "futex_wait failure")
ok := _futex_wait(f, expected)
_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 {
if u32(atomic_load_explicit(f, .Acquire)) != expected {
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)
}
/*
Wake up a single thread waiting on a futex.
*/
futex_signal :: proc "contextless" (f: ^Futex) {
_futex_signal(f)
}
/*
Wake up multiple threads waiting on a futex.
*/
futex_broadcast :: proc "contextless" (f: ^Futex) {
_futex_broadcast(f)
}
@@ -29,3 +29,7 @@ OpenPanel_setResolvesAliases :: proc "c" (self: ^OpenPanel, setting: BOOL) {
OpenPanel_setAllowsMultipleSelection :: proc "c" (self: ^OpenPanel, setting: BOOL) {
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 {
vendor: cpu_vendor,
cache_line_size: u32
cache_line_size: u32,
},
_core: struct {
model: u32,
+1
View File
@@ -16,6 +16,7 @@ CLOCK_REALTIME :: clockid_t(2)
CLOCK_THREAD_CPUTIME_ID :: clockid_t(3)
errno_t :: enum u16 {
NONE = 0,
// No error occurred. System call completed successfully.
SUCCESS = 0,
// Argument list too long.
+37
View File
@@ -152,6 +152,43 @@ foreign advapi32 {
cbData: DWORD,
) -> 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(
lpFileName: LPCWSTR,
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")
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 ---
DeleteObject :: proc(ho: HGDIOBJ) -> BOOL ---
SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
SetBkMode :: proc(hdc: HDC, mode: BKMODE) -> INT ---
CreateCompatibleDC :: proc(hdc: HDC) -> HDC ---
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 ---
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(
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: 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 ---
SetPixelFormat :: proc(hdc: HDC, format: INT, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> INT ---
DescribePixelFormat :: proc(hdc: HDC, iPixelFormat: INT, nBytes: UINT, ppfd: ^PIXELFORMATDESCRIPTOR) -> INT ---
SwapBuffers :: proc(hdc: HDC) -> BOOL ---
SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
GetDCBrushColor :: proc(hdc: HDC) -> COLORREF ---
PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL ---
Rectangle :: proc(hdc: HDC, left, top, right, bottom: c_int) -> BOOL ---
PatBlt :: proc(hdc: HDC, x, y, w, h: INT, rop: DWORD) -> BOOL ---
Rectangle :: proc(hdc: HDC, left, top, right, bottom: INT) -> BOOL ---
CreateFontW :: proc(
cHeight, cWidth, cEscapement, cOrientation, cWeight: c_int,
bItalic, bUnderline, bStrikeOut, iCharSet, iOutPrecision: DWORD,
iClipPrecision, iQuality, iPitchAndFamily: DWORD,
pszFaceName: LPCWSTR,
) -> HFONT ---
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
CreateFontW :: proc(cHeight, cWidth, cEscapement, cOrientation, cWeight: INT, bItalic, bUnderline, bStrikeOut, iCharSet, iOutPrecision: DWORD, iClipPrecision, iQuality, iPitchAndFamily: DWORD, pszFaceName: LPCWSTR) -> HFONT ---
CreateFontIndirectW :: proc(lplf: ^LOGFONTW) -> HFONT ---
CreateFontIndirectExW :: proc(unnamedParam1: ^ENUMLOGFONTEXDVW) -> HFONT ---
AddFontResourceW :: proc(unnamedParam1: LPCWSTR) -> INT ---
AddFontResourceExW :: proc(name: LPCWSTR, fl: DWORD, res: PVOID) -> INT ---
AddFontMemResourceEx :: proc(pFileView: PVOID, cjSize: DWORD, pvResrved: PVOID, pNumFonts: ^DWORD) -> HANDLE ---
EnumFontsW :: proc(hdc: HDC, lpLogfont: LPCWSTR, lpProc: FONTENUMPROCW, lParam: LPARAM) -> INT ---
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 ---
CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH ---
GetObjectW :: proc(h: HANDLE, c: c_int, pv: LPVOID) -> int ---
CreateCompatibleBitmap :: proc(hdc: HDC, cx, cy: c_int) -> HBITMAP ---
BitBlt :: proc(hdc: HDC, x, y, cx, cy: c_int, hdcSrc: HDC, x1, y1: c_int, rop: DWORD) -> BOOL ---
GetDIBits :: proc(hdc: HDC, hbm: HBITMAP, start, cLines: UINT, lpvBits: LPVOID, lpbmi: ^BITMAPINFO, usage: UINT) -> int ---
GetObjectW :: proc(h: HANDLE, c: INT, pv: LPVOID) -> int ---
CreateCompatibleBitmap :: proc(hdc: HDC, cx, cy: INT) -> HBITMAP ---
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 ---
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 {
return transmute(COLORREF)[4]u8{r, g, b, 0}
RGB :: #force_inline proc "contextless" (#any_int r, g, b: int) -> COLORREF {
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)
CIEXYZ :: struct {
ciexyzX: FXPT2DOT30,
ciexyzY: FXPT2DOT30,
ciexyzZ: FXPT2DOT30,
ciexyzX, ciexyzY, ciexyzZ: FXPT2DOT30,
}
CIEXYZTRIPLE :: struct {
ciexyzRed: CIEXYZ,
ciexyzGreen: CIEXYZ,
ciexyzBlue: CIEXYZ,
ciexyzRed, ciexyzGreen, ciexyzBlue: CIEXYZ,
}
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
@@ -138,3 +127,227 @@ BITMAPV5HEADER :: struct {
bV5ProfileSize: 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
GetNumberOfConsoleInputEvents :: proc(hConsoleInput: HANDLE, lpcNumberOfEvents: LPDWORD) -> BOOL ---
GetConsoleMode :: proc(hConsoleHandle: HANDLE,
lpMode: LPDWORD) -> BOOL ---
SetConsoleMode :: proc(hConsoleHandle: HANDLE,
dwMode: DWORD) -> BOOL ---
SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE,
dwCursorPosition: COORD) -> BOOL ---
SetConsoleTextAttribute :: proc(hConsoleOutput: HANDLE,
wAttributes: WORD) -> BOOL ---
GetConsoleCP :: proc() -> UINT ---
SetConsoleCP :: proc(wCodePageID: UINT) -> BOOL ---
GetConsoleOutputCP :: proc() -> UINT ---
SetConsoleOutputCP :: proc(wCodePageID: UINT) -> BOOL ---
GetConsoleMode :: proc(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL ---
SetConsoleMode :: proc(hConsoleHandle: HANDLE, dwMode: DWORD) -> BOOL ---
SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE, dwCursorPosition: COORD) -> BOOL ---
SetConsoleTextAttribute :: proc(hConsoleOutput: HANDLE, wAttributes: WORD) -> BOOL ---
GetConsoleCP :: proc() -> CODEPAGE ---
SetConsoleCP :: proc(wCodePageID: CODEPAGE) -> BOOL ---
GetConsoleOutputCP :: proc() -> CODEPAGE ---
SetConsoleOutputCP :: proc(wCodePageID: CODEPAGE) -> BOOL ---
FlushConsoleInputBuffer :: proc(hConsoleInput: HANDLE) -> BOOL ---
GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL ---
@@ -256,6 +252,7 @@ foreign kernel32 {
SetEnvironmentVariableW :: proc(n: LPCWSTR, v: LPCWSTR) -> BOOL ---
GetEnvironmentStringsW :: proc() -> LPWCH ---
FreeEnvironmentStringsW :: proc(env_ptr: LPWCH) -> BOOL ---
ExpandEnvironmentStringsW :: proc(lpSrc: LPCWSTR, lpDst: LPWSTR, nSize: DWORD) -> DWORD ---
GetModuleFileNameW :: proc(hModule: HMODULE, lpFilename: LPWSTR, nSize: DWORD) -> DWORD ---
CreateDirectoryW :: proc(
lpPathName: LPCWSTR,
@@ -440,12 +437,34 @@ foreign kernel32 {
GetFileAttributesExW :: proc(lpFileName: LPCWSTR, fInfoLevelId: GET_FILEEX_INFO_LEVELS, lpFileInformation: LPVOID) -> BOOL ---
GetSystemInfo :: proc(system_info: ^SYSTEM_INFO) ---
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 ---
LoadLibraryExW :: proc(c_str: LPCWSTR, hFile: HANDLE, dwFlags: LoadLibraryEx_Flags) -> HMODULE ---
FreeLibrary :: proc(h: HMODULE) -> BOOL ---
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 ---
GetLongPathNameW :: proc(short, long: LPCWSTR, len: DWORD) -> DWORD ---
+38
View File
@@ -23,8 +23,24 @@ foreign ntdll_lib {
Length: ULONG,
FileInformationClass: FILE_INFORMATION_CLASS,
) -> 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
IO_STATUS_BLOCK :: struct {
using _: struct #raw_union {
@@ -45,6 +61,12 @@ PROCESS_INFO_CLASS :: enum c_int {
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
FILE_INFORMATION_CLASS :: enum c_int {
@@ -134,6 +156,22 @@ FILE_INFORMATION_CLASS :: enum c_int {
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 {
+15 -1
View File
@@ -40,7 +40,8 @@ LPUNKNOWN :: ^IUnknown
@(default_calling_convention="system")
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() ---
CoCreateInstance :: proc(
@@ -52,4 +53,17 @@ foreign Ole32 {
) -> HRESULT ---
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 ---
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) ---
DragQueryPoint :: proc(hDrop: HDROP, ppt: ^POINT) -> BOOL ---
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
HMENU :: distinct HANDLE
HBRUSH :: distinct HANDLE
HPEN :: distinct HANDLE
HGDIOBJ :: distinct HANDLE
HBITMAP :: distinct HANDLE
HPALETTE :: distinct HANDLE
HGLOBAL :: distinct HANDLE
HHOOK :: distinct HANDLE
HWINEVENTHOOK :: distinct HANDLE
@@ -39,6 +41,9 @@ HKEY :: distinct HANDLE
HDESK :: distinct HANDLE
HFONT :: distinct HANDLE
HRGN :: distinct HANDLE
HRSRC :: distinct HANDLE
HWINSTA :: distinct HANDLE
HACCEL :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -139,11 +144,14 @@ LPSTR :: ^CHAR
LPWSTR :: ^WCHAR
OLECHAR :: WCHAR
LPOLESTR :: ^OLECHAR
LPCOLESTR :: LPCSTR
LPFILETIME :: ^FILETIME
LPWSABUF :: ^WSABUF
LPWSAOVERLAPPED :: distinct rawptr
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
LPCVOID :: rawptr
SCODE :: LONG
PSCODE :: ^SCODE
PACCESS_TOKEN :: PVOID
PSECURITY_DESCRIPTOR :: PVOID
@@ -699,6 +707,92 @@ FW_BLACK :: FW_HEAVY
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)
WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
@@ -932,6 +1026,16 @@ MF_RIGHTJUSTIFY :: 0x00004000
MF_MOUSESELECT :: 0x00008000
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
TPM_LEFTBUTTON :: 0x0000
TPM_RIGHTBUTTON :: 0x0002
@@ -1037,9 +1141,6 @@ WIN32_FIND_DATAW :: struct {
dwReserved1: DWORD,
cFileName: [MAX_PATH]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 {
@@ -2207,6 +2308,14 @@ CP_SYMBOL :: 42 // SYMBOL translations
CP_UTF7 :: 65000 // UTF-7 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
WC_ERR_INVALID_CHARS :: 128
@@ -2422,8 +2531,10 @@ REFIID :: ^GUID
REFGUID :: GUID
IID :: GUID
LPIID :: ^IID
CLSID :: GUID
REFCLSID :: ^CLSID
LPCLSID :: ^CLSID
CLSCTX_INPROC_SERVER :: 0x1
CLSCTX_INPROC_HANDLER :: 0x2
@@ -2453,6 +2564,7 @@ CLSCTX_RESERVED6 :: 0x1000000
CLSCTX_ACTIVATE_ARM32_SERVER :: 0x2000000
CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION :: 0x4000000
CLSCTX_PS_DLL :: 0x80000000
CLSCTX_ALL :: CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
WSAPROTOCOLCHAIN :: struct {
ChainLen: c_int,
@@ -2512,6 +2624,7 @@ OBJECT_ATTRIBUTES :: struct {
SecurityQualityOfService: rawptr,
}
PUNICODE_STRING :: ^UNICODE_STRING
UNICODE_STRING :: struct {
Length: u16 `fmt:"-"`,
MaximumLength: u16 `fmt:"-"`,
@@ -4389,7 +4502,7 @@ DNS_STATUS :: distinct DWORD // zero is success
DNS_INFO_NO_RECORDS :: 9501
DNS_QUERY_NO_RECURSION :: 0x00000004
DNS_RECORD :: struct {
DNS_RECORD :: struct { // aka DNS_RECORDA
pNext: ^DNS_RECORD,
pName: cstring,
wType: WORD,
@@ -4433,6 +4546,10 @@ SOCKADDR :: struct {
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 {
Disable = 0,
Enable = 1,
+216 -70
View File
@@ -6,19 +6,18 @@ foreign import user32 "system:User32.lib"
@(default_calling_convention="system")
foreign user32 {
GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassName: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
GetClassInfoExW :: proc(hInstance: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
GetClassLongW :: proc(hWnd: HWND, nIndex: INT) -> DWORD ---
SetClassLongW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG) -> DWORD ---
GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
GetWindowLongW :: proc(hWnd: HWND, nIndex: INT) -> 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 ---
IsWindowVisible :: proc(hWnd: HWND) -> BOOL ---
SetWinEventHook :: proc(
eventMin, eventMax: DWORD,
hmodWinEventProc: HMODULE,
@@ -38,10 +37,7 @@ foreign user32 {
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
X, Y, nWidth, nHeight: INT,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
@@ -50,24 +46,33 @@ foreign user32 {
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 ---
IsWindowVisible :: proc(hwnd: HWND) -> BOOL ---
IsWindowEnabled :: proc(hwnd: HWND) -> BOOL ---
IsIconic :: proc(hwnd: HWND) -> BOOL ---
BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
GetForegroundWindow :: proc() -> HWND ---
GetDesktopWindow :: proc() -> HWND ---
UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
GetActiveWindow :: proc() -> HWND ---
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 ---
TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
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 ---
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 ---
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 ---
@@ -94,33 +99,26 @@ foreign user32 {
LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
GetIconInfoExW :: proc(hIcon: HICON, piconinfo: PICONINFOEXW) -> BOOL ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> 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 ---
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 ---
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
ScreenToClient :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
SetWindowPos :: proc(
hWnd: HWND,
hWndInsertAfter: HWND,
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 ---
SetWindowPos :: proc(hWnd: HWND, hWndInsertAfter: HWND, X, Y, cx, cy: INT, uFlags: UINT) -> BOOL ---
MoveWindow :: proc(hWnd: HWND, X, Y, hWidth, hHeight: INT, bRepaint: BOOL) -> BOOL ---
GetSystemMetrics :: proc(nIndex: INT) -> INT ---
AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> 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 ---
@@ -130,18 +128,36 @@ foreign user32 {
GetWindowDC :: 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 ---
GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: c_int) -> HWND ---
GetDlgCtrlID :: proc(hWnd: HWND) -> INT ---
GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: INT) -> HWND ---
CreateMenu :: proc() -> HMENU ---
CreatePopupMenu :: proc() -> HMENU ---
DeleteMenu :: proc(hMenu: HMENU, uPosition: UINT, uFlags: UINT) -> 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 ---
GetMenu :: proc(hWnd: HWND) -> HMENU ---
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 ---
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 ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
@@ -154,34 +170,35 @@ foreign user32 {
ReleaseCapture :: proc() -> BOOL ---
TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL ---
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
GetKeyState :: proc(nVirtKey: INT) -> SHORT ---
GetAsyncKeyState :: proc(vKey: INT) -> SHORT ---
GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
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 ---
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 ---
KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
// MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int ---
MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int ---
// MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
// MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> INT ---
MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> 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) -> INT ---
ClipCursor :: proc(lpRect: LPRECT) -> 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 ---
when !intrinsics.is_package_imported("raylib") {
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 ---
MonitorFromPoint :: proc(pt: POINT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
@@ -191,6 +208,9 @@ foreign user32 {
EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
IsProcessDPIAware :: proc() -> BOOL ---
SetProcessDPIAware :: proc() -> BOOL ---
SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT ---
GetThreadDpiAwarenessContext :: proc() -> DPI_AWARENESS_CONTEXT ---
GetWindowDpiAwarenessContext :: proc(hwnd: HWND) -> DPI_AWARENESS_CONTEXT ---
@@ -225,14 +245,14 @@ foreign user32 {
lpdwResult: PDWORD_PTR,
) -> LRESULT ---
GetSysColor :: proc(nIndex: c_int) -> DWORD ---
GetSysColorBrush :: proc(nIndex: c_int) -> HBRUSH ---
SetSysColors :: proc(cElements: c_int, lpaElements: ^INT, lpaRgbValues: ^COLORREF) -> BOOL ---
GetSysColor :: proc(nIndex: INT) -> DWORD ---
GetSysColorBrush :: proc(nIndex: INT) -> HBRUSH ---
SetSysColors :: proc(cElements: INT, lpaElements: ^INT, lpaRgbValues: ^COLORREF) -> BOOL ---
MessageBeep :: proc(uType: UINT) -> BOOL ---
IsDialogMessageW :: proc(hDlg: HWND, lpMsg: LPMSG) -> BOOL ---
GetWindowTextLengthW :: proc(hWnd: HWND) -> c_int ---
GetWindowTextW :: proc(hWnd: HWND, lpString: LPWSTR, nMaxCount: c_int) -> c_int ---
GetWindowTextLengthW :: proc(hWnd: HWND) -> INT ---
GetWindowTextW :: proc(hWnd: HWND, lpString: LPWSTR, nMaxCount: INT) -> INT ---
SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL ---
CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL ---
@@ -245,12 +265,20 @@ foreign user32 {
GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
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 ---
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 ---
GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
@@ -259,21 +287,34 @@ foreign user32 {
CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
GetCursorInfo :: proc(pci: PCURSORINFO) -> BOOL ---
GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU ---
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 ---
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" (
lpClassName: LPCTSTR,
lpWindowName: LPCTSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
X: INT,
Y: INT,
nWidth: INT,
nHeight: INT,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
@@ -298,11 +339,11 @@ CreateWindowW :: #force_inline proc "system" (
when ODIN_ARCH == .amd64 {
@(default_calling_convention="system")
foreign user32 {
GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
GetClassLongPtrW :: proc(hWnd: HWND, nIndex: INT) -> ULONG_PTR ---
SetClassLongPtrW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG_PTR) -> ULONG_PTR ---
GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: INT) -> LONG_PTR ---
SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: INT, dwNewLong: LONG_PTR) -> LONG_PTR ---
}
} else when ODIN_ARCH == .i386 {
GetClassLongPtrW :: GetClassLongW
@@ -312,8 +353,8 @@ when ODIN_ARCH == .amd64 {
SetWindowLongPtrW :: SetWindowLongW
}
GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
return c_int(wParam) & 0xFFF0
GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> INT {
return INT(wParam) & 0xFFF0
}
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)
}
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 {
return cast(LPWSTR)uintptr(WORD(i))
}
@@ -512,8 +557,8 @@ WINDOWPLACEMENT :: struct {
flags: UINT,
showCmd: UINT,
ptMinPosition: POINT,
ptMaxPosition: POINT,
rcNormalPosition: RECT,
ptMaxPosition: POINT,
rcNormalPosition: RECT,
}
WINDOWINFO :: struct {
@@ -530,11 +575,20 @@ WINDOWINFO :: struct {
}
PWINDOWINFO :: ^WINDOWINFO
CURSORINFO :: struct {
cbSize: DWORD,
flags: DWORD,
hCursor: HCURSOR,
ptScreenPos: POINT,
}
PCURSORINFO :: ^CURSORINFO
DRAWTEXTPARAMS :: struct {
cbSize: UINT,
iTabLength: int,
iLeftMargin: int,
iRightMargin: int,
iTabLength: INT,
iLeftMargin: INT,
iRightMargin: INT,
uiLengthDrawn: UINT,
}
PDRAWTEXTPARAMS :: ^DRAWTEXTPARAMS
@@ -581,11 +635,103 @@ RedrawWindowFlags :: enum UINT {
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 {}
WinEventFlags :: bit_set[WinEventFlag; DWORD]
WinEventFlag :: enum DWORD {
SKIPOWNTHREAD = 0,
SKIPOWNPROCESS = 1,
INCONTEXT = 2,
SKIPOWNTHREAD = 0,
SKIPOWNPROCESS = 1,
INCONTEXT = 2,
}
+59 -2
View File
@@ -6,22 +6,79 @@ import "base:intrinsics"
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)
}
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)
}
// 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 {
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 {
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 {
return x << 8 | y
}
+227 -3
View File
@@ -1,6 +1,189 @@
// +build 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
NO_ERROR :: 0
SEC_E_OK : HRESULT : 0x00000000
@@ -42,14 +225,55 @@ ERROR_TIMEOUT : DWORD : 1460
ERROR_DATATYPE_MISMATCH : DWORD : 1629
ERROR_UNSUPPORTED_TYPE : DWORD : 1630
ERROR_NOT_SAME_OBJECT : DWORD : 1656
ERROR_PIPE_CONNECTED : DWORD : 0x80070217
ERROR_PIPE_CONNECTED : DWORD : 535
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 {
// The operation completed successfully.
SUCCESS = 0x0,
+16 -1
View File
@@ -270,7 +270,7 @@ LPHWAVEOUT :: ^HWAVEOUT
// https://learn.microsoft.com/en-us/windows/win32/multimedia/multimedia-timer-structures
MMTIME :: struct {
wType: UINT,
wType: MMTIME_TYPE,
u: struct #raw_union {
ms: DWORD,
sample: DWORD,
@@ -292,6 +292,21 @@ MMTIME :: struct {
}
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
MAXERRORLENGTH :: 256
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 {
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)
fmt.assertf(errno == os.ERROR_NONE, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, errno)
json_fd, err := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
fmt.assertf(err == nil, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, err)
defer os.close(json_fd)
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
undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) -> mem.Allocator_Error {
if s.builder != nil {
if s.builder == nil {
return nil
}
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.
**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)
```
This procedure rounds the duration to a specific 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 {
_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`.
**Inputs**:
- `d`: The duration to truncate.
- `m`: The unit to truncate to.
**Note**: Any duration can be supplied as a unit.
**Returns**:
Inputs:
- d: The duration to truncate.
- m: The unit to truncate to.
Returns:
- The duration `d`, truncated to the unit specified by `m`.
**Example**:
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.
Example:
time.duration_round(my_duration, time.Second)
*/
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
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
}
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.
*/
+1 -1
View File
@@ -359,7 +359,7 @@ control_flow :: proc() {
if false {
f, err := os.open("my_file.txt")
if err != os.ERROR_NONE {
if err != nil {
// handle error
}
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) {
bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
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].ext = copy_string(ha, STR_LIT("rc"));
}
@@ -2010,15 +2011,22 @@ gb_internal bool init_build_paths(String init_filename) {
}
}
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text));
// 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]);
defer (gb_free(ha, output_file.text));
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
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]);
defer (gb_free(ha, output_file.text));
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
case BuiltinProc_simd_add_sat:
case BuiltinProc_simd_sub_sat:
case BuiltinProc_simd_saturating_add:
case BuiltinProc_simd_saturating_sub:
case BuiltinProc_simd_bit_and:
case BuiltinProc_simd_bit_or:
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);
switch (id) {
case BuiltinProc_simd_add_sat:
case BuiltinProc_simd_sub_sat:
case BuiltinProc_simd_saturating_add:
case BuiltinProc_simd_saturating_sub:
if (!is_type_integer(elem)) {
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);
@@ -663,6 +663,91 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
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:
{
Operand x = {};
@@ -775,6 +860,29 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
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:
{
@@ -2460,7 +2568,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
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);
return false;
} else if (arg_count < 2) {
@@ -4302,8 +4410,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
break;
case BuiltinProc_add_sat:
case BuiltinProc_sub_sat:
case BuiltinProc_saturating_add:
case BuiltinProc_saturating_sub:
{
Operand x = {};
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 is_exact_value_zero(ExactValue const &v);
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -4457,6 +4459,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
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)) {
TEMPORARY_ALLOCATOR_GUARD();
@@ -5135,6 +5158,14 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Scope *import_scope = e->ImportName.scope;
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;
entity = scope_lookup_current(import_scope, entity_name);
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") {
if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(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);
*type = alloc_type_array(elem, count, generic_type);
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) {
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;
} else if (mode == Addressing_Value && is_type_proc(type)) {
} else if (mode == Addressing_Value && type != nullptr && is_type_proc(type)) {
expr->tav.value = value;
}
+28 -8
View File
@@ -70,8 +70,8 @@ enum BuiltinProcId {
BuiltinProc_overflow_sub,
BuiltinProc_overflow_mul,
BuiltinProc_add_sat,
BuiltinProc_sub_sat,
BuiltinProc_saturating_add,
BuiltinProc_saturating_sub,
BuiltinProc_sqrt,
BuiltinProc_fused_mul_add,
@@ -141,8 +141,8 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_shl_masked, // C logic
BuiltinProc_simd_shr_masked, // C logic
BuiltinProc_simd_add_sat, // saturation arithmetic
BuiltinProc_simd_sub_sat, // saturation arithmetic
BuiltinProc_simd_saturating_add, // saturation arithmetic
BuiltinProc_simd_saturating_sub, // saturation arithmetic
BuiltinProc_simd_bit_and,
BuiltinProc_simd_bit_or,
@@ -174,6 +174,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_reduce_or,
BuiltinProc_simd_reduce_xor,
BuiltinProc_simd_reduce_any,
BuiltinProc_simd_reduce_all,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
@@ -188,6 +191,12 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_lanes_rotate_left,
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
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_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("saturating_add"), 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("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_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_saturating_add"), 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_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_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_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_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(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

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