diff options
27 files changed, 304 insertions, 66 deletions
diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 9f3fcbf9a..6fecbaad9 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -267,7 +267,10 @@ non_zero_resize :: proc{ // Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity. @builtin -shrink :: proc{shrink_dynamic_array, shrink_map} +shrink :: proc{ + shrink_dynamic_array, + shrink_map, +} // `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation. @builtin @@ -794,7 +797,11 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar } // `inject_at` injects something into a dynamic array at a specified index and moves the previous elements after that index "across" -@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string} +@builtin inject_at :: proc{ + inject_at_elem, + inject_at_elems, + inject_at_elem_string, +} @@ -861,7 +868,6 @@ assign_at :: proc{ - // `clear_dynamic_array` will set the length of a passed dynamic array to `0` // // Note: Prefer the procedure group `clear`. @@ -1000,6 +1006,7 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i // If `len(array) < new_cap`, then `len(array)` will be left unchanged. // // Note: Prefer the procedure group `shrink` +@builtin shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc) } diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index 0ccc8fd9b..5078b85d0 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -615,7 +615,7 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no // `inject_at_soa` injects something into a dynamic SOA array at a specified index and moves the previous elements after that index "across" @builtin inject_at_soa :: proc{inject_at_elem_soa, inject_at_elems_soa} - +@builtin delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) when field_count != 0 { @@ -626,6 +626,7 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc return nil } +@builtin delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error { field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) when field_count != 0 { @@ -644,7 +645,7 @@ delete_soa :: proc{ delete_soa_dynamic_array, } - +@builtin clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) { field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) when field_count != 0 { diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 8956ae303..cf96098b8 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -148,6 +148,7 @@ mem_alloc_non_zeroed :: #force_no_inline proc(size: int, alignment: int = DEFAUL return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc) } +@builtin mem_free :: #force_no_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { if ptr == nil || allocator.procedure == nil { return nil @@ -172,7 +173,7 @@ mem_free_bytes :: #force_no_inline proc(bytes: []byte, allocator := context.allo return err } - +@builtin mem_free_all :: #force_no_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) { if allocator.procedure != nil { _, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc) @@ -341,7 +342,7 @@ memory_compare :: proc "contextless" (x, y: rawptr, n: int) -> int #no_bounds_ch case y == nil: return +1 } a, b := cast([^]byte)x, cast([^]byte)y - + n := uint(n) i := uint(0) m := uint(0) @@ -1409,4 +1410,3 @@ when .Address in ODIN_SANITIZER_FLAGS { __asan_unpoison_memory_region :: proc "system" (address: rawptr, size: uint) --- } } - diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 90119c699..4ef142037 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -184,10 +184,11 @@ print_rune :: #force_no_inline proc "contextless" (r: rune) -> int #no_bounds_ch print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check { + b :: u64(10) + u := x + a: [129]byte i := len(a) - b := u64(10) - u := x for u >= b { i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b] u /= b @@ -199,11 +200,9 @@ print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check { print_i64 :: #force_no_inline proc "contextless" (x: i64) #no_bounds_check { - b :: i64(10) - - u := x - neg := u < 0 - u = abs(u) + b :: u64(10) + u := u64(abs(x)) + neg := x < 0 a: [129]byte i := len(a) diff --git a/core/io/util.odin b/core/io/util.odin index 86a89cfb1..a82a80be0 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -21,12 +21,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri } write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { - buf: [32]byte + buf: [64]byte s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(w, s, n_written) } write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { - buf: [32]byte + buf: [65]byte s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(w, s, n_written) } @@ -39,12 +39,12 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> ( } write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { - buf: [39]byte + buf: [128]byte s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) return write_string(w, s, n_written) } write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { - buf: [40]byte + buf: [129]byte s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) return write_string(w, s, n_written) } diff --git a/core/net/errors.odin b/core/net/errors.odin index 4853327b0..de53640fc 100644 --- a/core/net/errors.odin +++ b/core/net/errors.odin @@ -149,7 +149,8 @@ TCP_Recv_Error :: enum i32 { Invalid_Argument, // The socket is not connected. Not_Connected, - // Connection was closed/broken/shutdown while receiving data. + // Connection was closed due to an error or shutdown. + // NOTE: a graceful close is indicated by a `0, nil` (0 bytes received and no error) return. Connection_Closed, // Timed out before being able to receive any data. Timeout, @@ -170,7 +171,8 @@ UDP_Recv_Error :: enum i32 { Insufficient_Resources, // Invalid socket or buffer given. Invalid_Argument, - // "Connection" was refused by remote, or closed/broken/shutdown while receiving data. + // "Connection" was refused, or closed due to an error. + // NOTE: a graceful close is indicated by a `0, nil` (0 bytes received and no error) return. Connection_Refused, // Timed out before being able to receive any data. Timeout, @@ -193,7 +195,7 @@ TCP_Send_Error :: enum i32 { Insufficient_Resources, // Invalid socket or buffer given. Invalid_Argument, - // Connection was closed/broken/shutdown while receiving data. + // Connection was closed/broken/shutdown while sending data. Connection_Closed, // The socket is not connected. Not_Connected, diff --git a/core/net/socket.odin b/core/net/socket.odin index 7e96ba2b2..edb47cd0b 100644 --- a/core/net/socket.odin +++ b/core/net/socket.odin @@ -193,21 +193,36 @@ close :: proc(socket: Any_Socket) { _close(socket) } +/* + Receive data into a buffer from a TCP socket. + + If no error occurs, `recv_tcp` returns the number of bytes received and `buf` will contain this data received. + If the connection has been gracefully closed, the return value is `0, nil` (0 bytes read and no error). +*/ recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) { return _recv_tcp(socket, buf) } +/* + Receive data into a buffer from a UDP socket. + + If no error occurs, `recv_udp` returns the number of bytes received and `buf` will contain this data received. + If the "connection" has been gracefully closed, the return value is `0, nil` (0 bytes read and no error). +*/ recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) { return _recv_udp(socket, buf) } /* - Receive data from into a buffer from any socket. + Receive data into a buffer from any socket. Note: `remote_endpoint` parameter is non-nil only if the socket type is UDP. On TCP sockets it will always return `nil`. - Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error` + Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`. + + If no error occurs, `recv_any` returns the number of bytes received and `buf` will contain this data received. + If the connection has been gracefully closed, the return value is `0, nil, nil` (0 bytes read and no error). */ recv_any :: proc(socket: Any_Socket, buf: []byte) -> ( bytes_read: int, diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 0d2f28642..9a969f07e 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -364,33 +364,33 @@ _read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) { handle := _handle(&f.file) - single_read_length: win32.DWORD total_read: int sync.shared_guard(&f.rw_mutex) // multiple readers if sync.guard(&f.p_mutex) { to_read := min(win32.DWORD(length), MAX_RW) - ok: win32.BOOL - if f.kind == .Console { - n, cerr := read_console(handle, p[total_read:][:to_read]) - total_read += n - if cerr != nil { - return i64(total_read), cerr + switch f.kind { + case .Console: + // NOTE(laytan): at least for now, just use ReadFile, it seems to work fine, + // but, there may be issues with certain situations that we need to get reproductions for. + // total_read, err = read_console(handle, p[total_read:][:to_read]) + fallthrough + case .Pipe, .File: + single_read_length: win32.DWORD + ok := win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) + if ok { + total_read += int(single_read_length) + } else { + err = _get_platform_error() } - } else { - ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) } + } - if single_read_length > 0 && ok { - total_read += int(single_read_length) - } else if single_read_length == 0 && ok { - // ok and 0 bytes means EOF: - // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file - err = .EOF - } else { - err = _get_platform_error() - } + if total_read == 0 && err == nil { + // ok and 0 bytes means EOF: + // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file + err = .EOF } return i64(total_read), err diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 5fbff08c2..b39c6ac6b 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -2003,4 +2003,16 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ runtime.print_typeid(a.id) runtime.print_string("\n") return true +} + + +@(require_results) +default_map_hash_by_ptr :: proc(ptr: ^$T, seed: uintptr = 0) -> uintptr where intrinsics.type_is_comparable(T) { + assert(ptr != nil) + info := intrinsics.type_map_info(map[T]struct{}) + + h := u64(seed) + runtime.INITIAL_HASH_SEED + actual_seed := uintptr(h) | uintptr(uintptr(h) == 0) + + return info.key_hasher(ptr, actual_seed) }
\ No newline at end of file diff --git a/core/strings/builder.odin b/core/strings/builder.odin index da9b6df27..ce636a2a1 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -880,6 +880,20 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo } if len(old) == 0 { + // NOTE(bill): reserve the necessary memory + found := 0 + for i := 0; i <= len(b.buf); i += len(new)+1 { + if n > 0 && found == n { + break + } + found += 1 + } + if found == 0 { + return + } + reserve(&b.buf, len(b.buf) + len(new)*found) or_return + + for i := 0; i <= len(b.buf); i += len(new)+1 { if n > 0 && replaced == n { break @@ -891,6 +905,27 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo replaced += 1 } } else { + if len(new) > len(old) { + // NOTE(bill): reserve the necessary memory + found := 0 + for i := 0; i < len(b.buf); /**/ { + if n > 0 && found == n { + break + } + + j := index(string(b.buf[i:]), old) + if j < 0 { + break + } + i += j+len(old) + found += 1 + } + if found == 0 { + return + } + reserve(&b.buf, len(b.buf) + (len(new)-len(old))*found) or_return + } + for i := 0; i < len(b.buf); /**/ { if n > 0 && replaced == n { break @@ -901,7 +936,7 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo break } - if len(new) >= len(old) { + if len(new) > len(old) { resize(&b.buf, len(b.buf) + len(new)-len(old)) or_return } diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin index 2295e46b8..cdda7b3e4 100644 --- a/core/sys/darwin/Foundation/NSApplication.odin +++ b/core/sys/darwin/Foundation/NSApplication.odin @@ -1,21 +1,9 @@ package objc_Foundation -foreign import "system:Foundation.framework" - import "base:intrinsics" import "base:runtime" import "core:strings" -RunLoopMode :: ^String - -@(link_prefix="NS") -foreign Foundation { - RunLoopCommonModes: RunLoopMode - DefaultRunLoopMode: RunLoopMode - EventTrackingRunLoopMode: RunLoopMode - ModalPanelRunLoopMode: RunLoopMode -} - ActivationPolicy :: enum UInteger { Regular = 0, Accessory = 1, @@ -206,6 +194,13 @@ Application_updateWindows :: proc "c" (self: ^Application) { msgSend(nil, self, "updateWindows") } +@(objc_type=Application, objc_name="sendAction") +Application_sendAction :: proc "c" (self: ^Application, action: SEL, to: id, from: id) { + msgSend(nil, self, "sendAction:to:from:", action, to, from) +} + + + @(objc_class="NSRunningApplication") RunningApplication :: struct {using _: Object} diff --git a/core/sys/darwin/Foundation/NSCursor.odin b/core/sys/darwin/Foundation/NSCursor.odin new file mode 100644 index 000000000..ad2decdca --- /dev/null +++ b/core/sys/darwin/Foundation/NSCursor.odin @@ -0,0 +1,26 @@ +package objc_Foundation + +@(objc_class="NSCursor") +Cursor :: struct {using _: Object} + +@(objc_type=Cursor, objc_name="set") +Cursor_set :: proc(self: ^Cursor) { + msgSend(EventType, self, "set") +} +@(objc_type=Cursor, objc_name="currentCursor", objc_is_class_method=true) +Cursor_currentCursor :: proc "c" () -> ^Cursor { + return msgSend(^Cursor, Cursor, "currentCursor") +} +@(objc_type=Cursor, objc_name="IBeamCursor", objc_is_class_method=true) +Cursor_IBeamCursor :: proc "c" () -> ^Cursor { + return msgSend(^Cursor, Cursor, "IBeamCursor") +} +@(objc_type=Cursor, objc_name="arrowCursor", objc_is_class_method=true) +Cursor_arrowCursor :: proc "c" () -> ^Cursor { + return msgSend(^Cursor, Cursor, "arrowCursor") +} +@(objc_type=Cursor, objc_name="pointingHandCursor", objc_is_class_method=true) +Cursor_pointingHandCursor :: proc "c" () -> ^Cursor { + return msgSend(^Cursor, Cursor, "pointingHandCursor") +} + diff --git a/core/sys/darwin/Foundation/NSEvent.odin b/core/sys/darwin/Foundation/NSEvent.odin index 3bd0c1879..952a1726c 100644 --- a/core/sys/darwin/Foundation/NSEvent.odin +++ b/core/sys/darwin/Foundation/NSEvent.odin @@ -334,6 +334,11 @@ Event_locationInWindow :: proc "c" (self: ^Event) -> Point { return msgSend(Point, self, "locationInWindow") } +@(objc_type=Event, objc_name="mouseLocation", objc_is_class_method=true) +Event_mouseLocation :: proc "c" () -> Point { + return msgSend(Point, Event, "mouseLocation") +} + @(objc_type=Event, objc_name="deltaX") Event_deltaX :: proc "c" (self: ^Event) -> Float { diff --git a/core/sys/darwin/Foundation/NSObject.odin b/core/sys/darwin/Foundation/NSObject.odin index 31ece47a1..7a423e757 100644 --- a/core/sys/darwin/Foundation/NSObject.odin +++ b/core/sys/darwin/Foundation/NSObject.odin @@ -81,6 +81,12 @@ debugDescription :: proc "c" (self: ^Object) -> ^String { return nil } + +@(objc_type=Object, objc_name="performSelectorOnMainThread") +performSelectorOnMainThread :: proc "c" (self: ^Object, aSelector: SEL, arg: id, wait: BOOL) { + msgSend(^String, self, "performSelectorOnMainThread:withObject:waitUntilDone:", aSelector, arg, wait) +} + bridgingCast :: proc "c" ($T: typeid, obj: ^Object) where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) { return (T)(obj) } @@ -88,4 +94,4 @@ bridgingCast :: proc "c" ($T: typeid, obj: ^Object) where intrinsics.type_is_poi @(objc_class="NSCoder") Coder :: struct {using _: Object} -// TODO(bill): Implement all the methods for this massive type
\ No newline at end of file +// TODO(bill): Implement all the methods for this massive type diff --git a/core/sys/darwin/Foundation/NSRunLoop.odin b/core/sys/darwin/Foundation/NSRunLoop.odin new file mode 100644 index 000000000..a9c47bd0e --- /dev/null +++ b/core/sys/darwin/Foundation/NSRunLoop.odin @@ -0,0 +1,27 @@ +package objc_Foundation + +foreign import "system:Foundation.framework" + +RunLoopMode :: ^String + +@(link_prefix="NS") +foreign Foundation { + RunLoopCommonModes: RunLoopMode + DefaultRunLoopMode: RunLoopMode + EventTrackingRunLoopMode: RunLoopMode + ModalPanelRunLoopMode: RunLoopMode +} + +@(objc_class="NSRunLoop") +RunLoop :: struct { using _: Object } + +@(objc_type=RunLoop, objc_name="mainRunLoop", objc_is_class_method=true) +RunLoop_mainRunLoop :: proc() -> ^RunLoop { + return msgSend(^RunLoop, RunLoop, "mainRunLoop") +} + +@(objc_type=RunLoop, objc_name="addTimerForMode") +RunLoop_addTimerForMode :: proc(self: ^RunLoop, timer: ^Timer, forMode: RunLoopMode) { + msgSend(nil, self, "addTimer:forMode:", timer, forMode) +} + diff --git a/core/sys/darwin/Foundation/NSTimer.odin b/core/sys/darwin/Foundation/NSTimer.odin new file mode 100644 index 000000000..2edcf34c3 --- /dev/null +++ b/core/sys/darwin/Foundation/NSTimer.odin @@ -0,0 +1,15 @@ +package objc_Foundation + +@(objc_class="NSTimer") +Timer :: struct { using _: Object } + +@(objc_type=Timer, objc_name="scheduledTimerWithTimeIntervalRepeatsBlock", objc_is_class_method=true) +Timer_scheduledTimerWithTimeIntervalRepeatsBlock :: proc(interval: TimeInterval, repeats: BOOL, block: ^Block) -> ^Timer { + return msgSend(^Timer, Timer, "scheduledTimerWithTimeInterval:repeats:block:") +} + +@(objc_type=Timer, objc_name="scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeat", objc_is_class_method=true) +Timer_scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeat :: proc(interval: TimeInterval, aTarget: id, aSelector: SEL, userInfo: id, repeats: BOOL) -> ^Timer { + return msgSend(^Timer, Timer, "scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:", interval, aTarget, aSelector, userInfo, repeats) +} + diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin index 8317c7bb1..f39faca0a 100644 --- a/core/sys/darwin/Foundation/NSWindow.odin +++ b/core/sys/darwin/Foundation/NSWindow.odin @@ -699,6 +699,14 @@ View_convertPointFromView :: proc "c" (self: ^View, point: Point, view: ^View) - View_addSubview :: proc "c" (self: ^View, view: ^View) { msgSend(nil, self, "addSubview:", view) } +@(objc_type=View, objc_name="isFlipped") +View_isFlipped :: proc "c" (self: ^View) -> BOOL { + return msgSend(BOOL, self, "isFlipped") +} +@(objc_type=View, objc_name="setIsFlipped") +View_setIsFlipped :: proc "c" (self: ^View, flipped: BOOL) { + msgSend(nil, self, "setIsFlipped:", flipped) +} @(objc_class="NSWindow") Window :: struct {using _: Responder} diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index 04305ece1..faeda6f43 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -2992,15 +2992,36 @@ epoll_pwait :: proc(epfd: Fd, events: [^]EPoll_Event, count: i32, timeout: i32, // TODO(flysand): signalfd -// TODO(flysand): timerfd_create +/* + Create Linux file descriptor based timer. + Available since Linux 2.6.25 +*/ +timerfd_create :: proc "contextless" (clock_id: Clock_Id, flags: Open_Flags) -> (Fd, Errno) { + ret := syscall(SYS_timerfd_create, clock_id, transmute(u32)flags) + return errno_unwrap2(ret, Fd) +} // TODO(flysand): eventfd // TODO(flysand): fallocate -// TODO(flysand): timerfd_settime +/* + Arm/disarm the state of the Linux file descriptor based timer. + Available since Linux 2.6.25 +*/ +timerfd_settime :: proc "contextless" (fd: Fd, flags: ITimer_Flags, new_value: ^ITimer_Spec, old_value: ^ITimer_Spec) -> Errno { + ret := syscall(SYS_timerfd_settime, fd, transmute(u32)flags, new_value, old_value) + return Errno(-ret) +} -// TODO(flysand): timerfd_gettime +/* + Get the state of the Linux file descriptor based timer. + Available since Linux 2.6.25 +*/ +timerfd_gettime :: proc "contextless" (fd: Fd, curr_value: ^ITimer_Spec) -> Errno { + ret := syscall(SYS_timerfd_gettime, fd, curr_value) + return Errno(-ret) +} // TODO(flysand): accept4 diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin index a9e582b51..bb400c5a9 100644 --- a/core/sys/posix/poll.odin +++ b/core/sys/posix/poll.odin @@ -25,7 +25,7 @@ foreign lib { poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: c.int) -> c.int --- } -when ODIN_OS == .Haiku { +when ODIN_OS == .Haiku || ODIN_OS == .Linux { nfds_t :: c.ulong } else { nfds_t :: c.uint diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index 43a6b002a..ef203e24d 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -2107,7 +2107,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory if (exports.memory) { if (wasmMemoryInterface.memory) { - console.warn("WASM module exports memory, but `runWasm` was given an interface with existing memory too"); + console.warn('WASM module exports memory, but `runWasm` was given an interface with existing memory too. Did you mean to use `-extra-linker-flags:"--import-memory"` to tell the compiler not to export memory?'); } wasmMemoryInterface.setMemory(exports.memory); } diff --git a/src/checker.cpp b/src/checker.cpp index 4d5482933..33121b453 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3581,7 +3581,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "test") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->test = true; return true; @@ -3629,13 +3629,13 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "init") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->init = true; return true; } else if (name == "fini") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->fini = true; return true; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c38150e31..1cde65640 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2085,7 +2085,7 @@ gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedur continue; } - if (type_size_of(e->type) > 8) { + if (false && type_size_of(e->type) > 8) { String ename = lb_get_entity_name(m, e); gbString name = gb_string_make(permanent_allocator(), ""); name = gb_string_appendc(name, "__$startup$"); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 33ad2ee8d..9ddbd1f9c 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2868,9 +2868,13 @@ gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice<lb GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named); objc_class = entity_from_expr(se->expr); - GB_ASSERT(objc_class); GB_ASSERT(objc_class->kind == Entity_TypeName); + + if (objc_class->TypeName.is_type_alias) { + objc_class = objc_class->type->Named.type_name; + } + GB_ASSERT(objc_class->TypeName.objc_class_name != ""); } diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index ec45744f3..9b3973a60 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -624,6 +624,41 @@ test_nonblocking_option :: proc(t: ^testing.T) { } } +// Test that when the server closes it's connection, the client's next receive is `0, nil` to indicate a correct close. +@(test) +test_connection_close :: proc(t: ^testing.T) { + server, listen_err := net.listen_tcp({address=net.IP4_Address{127, 0, 0, 1}, port=0}) + testing.expect_value(t, listen_err, nil) + defer net.close(server) + + server_ep, bound_endpoint_err := net.bound_endpoint(server) + testing.expect_value(t, bound_endpoint_err, nil) + + client, dial_err := net.dial_tcp(server_ep) + testing.expect_value(t, dial_err, nil) + defer net.close(client) + + server_client, _, accept_err := net.accept_tcp(server) + testing.expect_value(t, accept_err, nil) + + send_buf: [512]byte = 1 + sent, send_err := net.send(server_client, send_buf[:]) + testing.expect_value(t, sent, 512) + testing.expect_value(t, send_err, nil) + net.close(server_client) + + recv_buf: [512]byte = --- + received, recv_err := net.recv(client, recv_buf[:]) + testing.expect_value(t, received, 512) + testing.expect_value(t, recv_err, nil) + + testing.expect_value(t, recv_buf, send_buf) + + received, recv_err = net.recv(client, recv_buf[:]) + testing.expect_value(t, received, 0) + testing.expect_value(t, recv_err, nil) +} + @(private) address_to_binstr :: proc(address: net.Address) -> (binstr: string) { switch t in address { diff --git a/tests/core/sys/posix/structs/structs.c b/tests/core/sys/posix/structs/structs.c index ac771057e..6bb7df29e 100644 --- a/tests/core/sys/posix/structs/structs.c +++ b/tests/core/sys/posix/structs/structs.c @@ -74,6 +74,7 @@ int main(int argc, char *argv[]) printf("ai_canonname %zu\n", offsetof(struct addrinfo, ai_canonname)); printf("pollfd %zu %zu\n", sizeof(struct pollfd), _Alignof(struct pollfd)); + printf("nfds_t %zu %zu\n", sizeof(nfds_t), _Alignof(nfds_t)); printf("passwd %zu %zu\n", sizeof(struct passwd), _Alignof(struct passwd)); diff --git a/tests/core/sys/posix/structs/structs.odin b/tests/core/sys/posix/structs/structs.odin index 55369e386..64833c437 100644 --- a/tests/core/sys/posix/structs/structs.odin +++ b/tests/core/sys/posix/structs/structs.odin @@ -41,6 +41,8 @@ main :: proc() { fmt.println("ai_canonname", offset_of(posix.addrinfo, ai_canonname)) fmt.println("pollfd", size_of(posix.pollfd), align_of(posix.pollfd)) + fmt.println("nfds_t", size_of(posix.nfds_t), align_of(posix.nfds_t)) + fmt.println("passwd", size_of(posix.passwd), align_of(posix.passwd)) when ODIN_OS != .Haiku { diff --git a/vendor/darwin/QuartzCore/QuartzCore.odin b/vendor/darwin/QuartzCore/QuartzCore.odin index 0e54b3d30..64e8e66d3 100644 --- a/vendor/darwin/QuartzCore/QuartzCore.odin +++ b/vendor/darwin/QuartzCore/QuartzCore.odin @@ -47,6 +47,10 @@ MetalLayer_pixelFormat :: proc "c" (self: ^MetalLayer) -> MTL.PixelFormat { MetalLayer_setPixelFormat :: proc "c" (self: ^MetalLayer, pixelFormat: MTL.PixelFormat) { msgSend(nil, self, "setPixelFormat:", pixelFormat) } +@(objc_type=MetalLayer, objc_name="setColorSpace") +MetalLayer_setColorSpace :: proc "c" (self: ^MetalLayer, colorspace: NS.id) { + msgSend(nil, self, "setColorspace:", colorspace) +} @(objc_type=MetalLayer, objc_name="framebufferOnly") MetalLayer_framebufferOnly :: proc "c" (self: ^MetalLayer) -> NS.BOOL { @@ -126,3 +130,21 @@ MetalDrawable_addPresentedHandler :: proc "c" (self: ^MetalDrawable, block: Draw msgSend(nil, self, "addPresentedHandler:", block) } +@(objc_class="CATransaction") +Transaction :: struct { using _: NS.Object } + +@(objc_type=Transaction, objc_name="begin", objc_is_class_method=true) +transaction_begin :: proc() { + msgSend(nil, Transaction, "begin") +} + +@(objc_type=Transaction, objc_name="commit", objc_is_class_method=true) +transaction_commit :: proc() { + msgSend(nil, Transaction, "commit") +} + +@(objc_type=Transaction, objc_name="flush", objc_is_class_method=true) +transaction_flush :: proc() { + msgSend(nil, Transaction, "flush") +} + |