package nbio import "core:net" import "core:os" import "core:time" /* The main IO type that holds the platform dependant implementation state passed around most procedures in this package */ IO :: _IO /* Initializes the IO type, allocates different things per platform needs *Allocates Using Provided Allocator* Inputs: - io: The IO struct to initialize - allocator: (default: context.allocator) Returns: - err: An error code when something went wrong with the setup of the platform's IO API, 0 otherwise */ init :: proc(io: ^IO, allocator := context.allocator) -> (err: os.Errno) { return _init(io, allocator) } /* The place where the magic happens, each time you call this the IO implementation checks its state and calls any callbacks which are ready. You would typically call this in a loop Inputs: - io: The IO instance to tick Returns: - err: An error code when something went when retrieving events, 0 otherwise */ tick :: proc(io: ^IO) -> os.Errno { return _tick(io) } /* Returns the number of in-progress IO to be completed. */ num_waiting :: #force_inline proc(io: ^IO) -> int { return _num_waiting(io) } /* Deallocates anything that was allocated when calling init() Inputs: - io: The IO instance to deallocate *Deallocates with the allocator that was passed with the init() call* */ destroy :: proc(io: ^IO) { _destroy(io) } /* The callback for a "next tick" event Inputs: - user: A passed through pointer from initiation to its callback */ On_Next_Tick :: #type proc(user: rawptr) /* Schedules a callback to be called during the next tick of the event loop. Inputs: - io: The IO instance to use - user: A pointer that will be passed through to the callback, free to use by you and untouched by us */ next_tick :: proc(io: ^IO, user: rawptr, callback: On_Next_Tick) -> ^Completion { return _next_tick(io, user, callback) } /* The callback for non blocking `timeout` calls Inputs: - user: A passed through pointer from initiation to its callback */ On_Timeout :: #type proc(user: rawptr) /* Schedules a callback to be called after the given duration elapses. The accuracy depends on the time between calls to `tick`. When you call it in a loop with no blocks or very expensive calculations in other scheduled event callbacks it is reliable to about a ms of difference (so timeout of 10ms would almost always be ran between 10ms and 11ms). Inputs: - io: The IO instance to use - dur: The minimum duration to wait before calling the given callback - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Timeout` for its arguments */ timeout :: proc(io: ^IO, dur: time.Duration, user: rawptr, callback: On_Timeout) { _timeout(io, dur, user, callback) } /* Creates a socket, sets non blocking mode and relates it to the given IO Inputs: - io: The IO instance to initialize the socket on/with - family: Should this be an IP4 or IP6 socket - protocol: The type of socket (TCP or UDP) Returns: - socket: The opened socket - err: A network error that happened while opening */ open_socket :: proc( io: ^IO, family: net.Address_Family, protocol: net.Socket_Protocol, ) -> ( socket: net.Any_Socket, err: net.Network_Error, ) { return _open_socket(io, family, protocol) } /* Creates a socket, sets non blocking mode, relates it to the given IO, binds the socket to the given endpoint and starts listening Inputs: - io: The IO instance to initialize the socket on/with - endpoint: Where to bind the socket to Returns: - socket: The opened, bound and listening socket - err: A network error that happened while opening */ open_and_listen_tcp :: proc(io: ^IO, ep: net.Endpoint) -> (socket: net.TCP_Socket, err: net.Network_Error) { family := net.family_from_endpoint(ep) sock := open_socket(io, family, .TCP) or_return socket = sock.(net.TCP_Socket) if err = net.bind(socket, ep); err != nil { close(io, socket) return } if err = listen(socket); err != nil { close(io, socket) } return } /* Starts listening on the given socket Inputs: - socket: The socket to start listening - backlog: The amount of events to keep in the backlog when they are not consumed Returns: - err: A network error that happened when starting listening */ listen :: proc(socket: net.TCP_Socket, backlog := 1000) -> (err: net.Network_Error) { return _listen(socket, backlog) } /* The callback for non blocking `close` requests Inputs: - user: A passed through pointer from initiation to its callback - ok: Whether the operation suceeded sucessfully */ On_Close :: #type proc(user: rawptr, ok: bool) @private empty_on_close :: proc(_: rawptr, _: bool) {} /* A union of types that are `close`'able by this package */ Closable :: union #no_nil { net.TCP_Socket, net.UDP_Socket, net.Socket, os.Handle, } /* Closes the given `Closable` socket or file handle that was originally created by this package. *Due to platform limitations, you must pass a `Closable` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The `Closable` socket or handle (created using/by this package) to close - user: An optional pointer that will be passed through to the callback, free to use by you and untouched by us - callback: An optional callback that is called when the operation completes, see docs for `On_Close` for its arguments */ close :: proc(io: ^IO, fd: Closable, user: rawptr = nil, callback: On_Close = empty_on_close) { _close(io, fd, user, callback) } /* The callback for non blocking `accept` requests Inputs: - user: A passed through pointer from initiation to its callback - client: The socket to communicate through with the newly accepted client - source: The origin of the client - err: A network error that occured during the accept process */ On_Accept :: #type proc(user: rawptr, client: net.TCP_Socket, source: net.Endpoint, err: net.Network_Error) /* Using the given socket, accepts the next incoming connection, calling the callback when that happens *Due to platform limitations, you must pass a socket that was opened using the `open_socket` and related procedures from this package* Inputs: - io: The IO instance to use - socket: A bound and listening socket *that was created using this package* - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Accept` for its arguments */ accept :: proc(io: ^IO, socket: net.TCP_Socket, user: rawptr, callback: On_Accept) { _accept(io, socket, user, callback) } /* The callback for non blocking `connect` requests Inputs: - user: A passed through pointer from initiation to its callback - socket: A socket that is connected to the given endpoint in the `connect` call - err: A network error that occured during the connect call */ On_Connect :: #type proc(user: rawptr, socket: net.TCP_Socket, err: net.Network_Error) /* Connects to the given endpoint, calling the given callback once it has been done Inputs: - io: The IO instance to use - endpoint: An endpoint to connect a socket to - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Connect` for its arguments */ connect :: proc(io: ^IO, endpoint: net.Endpoint, user: rawptr, callback: On_Connect) { _, err := _connect(io, endpoint, user, callback) if err != nil { callback(user, {}, err) } } /* The callback for non blocking `recv` requests Inputs: - user: A passed through pointer from initiation to its callback - received: The amount of bytes that were read and added to the given buf - udp_client: If the given socket was a `net.UDP_Socket`, this will be the client that was received from - err: A network error if it occured */ On_Recv :: #type proc(user: rawptr, received: int, udp_client: Maybe(net.Endpoint), err: net.Network_Error) /* Receives from the given socket, at most `len(buf)` bytes, and calls the given callback *Due to platform limitations, you must pass a `net.TCP_Socket` or `net.UDP_Socket` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - socket: Either a `net.TCP_Socket` or a `net.UDP_Socket` (that was opened/returned by this package) to receive from - buf: The buffer to put received bytes into - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Recv` for its arguments */ recv :: proc(io: ^IO, socket: net.Any_Socket, buf: []byte, user: rawptr, callback: On_Recv) { _recv(io, socket, buf, user, callback) } /* Receives from the given socket until the given buf is full or an error occurred, and calls the given callback *Due to platform limitations, you must pass a `net.TCP_Socket` or `net.UDP_Socket` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - socket: Either a `net.TCP_Socket` or a `net.UDP_Socket` (that was opened/returned by this package) to receive from - buf: The buffer to put received bytes into - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Recv` for its arguments */ recv_all :: proc(io: ^IO, socket: net.Any_Socket, buf: []byte, user: rawptr, callback: On_Recv) { _recv(io, socket, buf, user, callback, all = true) } /* The callback for non blocking `send` and `send_all` requests Inputs: - user: A passed through pointer from initiation to its callback - sent: The amount of bytes that were sent over the connection - err: A network error if it occured */ On_Sent :: #type proc(user: rawptr, sent: int, err: net.Network_Error) /* Sends at most `len(buf)` bytes from the given buffer over the socket connection, and calls the given callback *Prefer using the `send` proc group* *Due to platform limitations, you must pass a `net.TCP_Socket` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - socket: a `net.TCP_Socket` (that was opened/returned by this package) to send to - buf: The buffer send - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Sent` for its arguments */ send_tcp :: proc(io: ^IO, socket: net.TCP_Socket, buf: []byte, user: rawptr, callback: On_Sent) { _send(io, socket, buf, user, callback) } /* Sends at most `len(buf)` bytes from the given buffer over the socket connection to the given endpoint, and calls the given callback *Prefer using the `send` proc group* *Due to platform limitations, you must pass a `net.UDP_Socket` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - endpoint: The endpoint to send bytes to over the socket - socket: a `net.UDP_Socket` (that was opened/returned by this package) to send to - buf: The buffer send - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Sent` for its arguments */ send_udp :: proc( io: ^IO, endpoint: net.Endpoint, socket: net.UDP_Socket, buf: []byte, user: rawptr, callback: On_Sent, ) { _send(io, socket, buf, user, callback, endpoint) } /* Sends at most `len(buf)` bytes from the given buffer over the socket connection, and calls the given callback *Due to platform limitations, you must pass a `net.TCP_Socket` or `net.UDP_Socket` that was opened/returned using/by this package* */ send :: proc { send_udp, send_tcp, } /* Sends the bytes from the given buffer over the socket connection, and calls the given callback This will keep sending until either an error or the full buffer is sent *Prefer using the `send` proc group* *Due to platform limitations, you must pass a `net.TCP_Socket` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - socket: a `net.TCP_Socket` (that was opened/returned by this package) to send to - buf: The buffer send - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Sent` for its arguments */ send_all_tcp :: proc(io: ^IO, socket: net.TCP_Socket, buf: []byte, user: rawptr, callback: On_Sent) { _send(io, socket, buf, user, callback, all = true) } /* Sends the bytes from the given buffer over the socket connection to the given endpoint, and calls the given callback This will keep sending until either an error or the full buffer is sent *Prefer using the `send` proc group* *Due to platform limitations, you must pass a `net.UDP_Socket` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - endpoint: The endpoint to send bytes to over the socket - socket: a `net.UDP_Socket` (that was opened/returned by this package) to send to - buf: The buffer send - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Sent` for its arguments */ send_all_udp :: proc( io: ^IO, endpoint: net.Endpoint, socket: net.UDP_Socket, buf: []byte, user: rawptr, callback: On_Sent, ) { _send(io, socket, buf, user, callback, endpoint, all = true) } /* Sends the bytes from the given buffer over the socket connection, and calls the given callback This will keep sending until either an error or the full buffer is sent *Due to platform limitations, you must pass a `net.TCP_Socket` or `net.UDP_Socket` that was opened/returned using/by this package* */ send_all :: proc { send_all_udp, send_all_tcp, } /* Opens a file hande, sets non blocking mode and relates it to the given IO *The perm argument is only used when on the darwin or linux platforms, when on Windows you can't use the os.S_\* constants because they aren't declared* *To prevent compilation errors on Windows, you should use a `when` statement around using those constants and just pass 0* Inputs: - io: The IO instance to connect the opened file to - mode: The file mode (default: os.O_RDONLY) - perm: The permissions to use when creating a file (default: 0) Returns: - handle: The file handle - err: The error code when an error occured, 0 otherwise */ open :: proc(io: ^IO, path: string, mode: int = os.O_RDONLY, perm: int = 0) -> (handle: os.Handle, err: os.Errno) { return _open(io, path, mode, perm) } /* Where to seek from Options: - Set: sets the offset to the given value - Curr: adds the given offset to the current offset - End: adds the given offset to the end of the file */ Whence :: enum { Set, Curr, End, } /* Seeks the given handle according to the given offset and whence, so that subsequent read and writes *USING THIS PACKAGE* will do so at that offset *Some platforms require this package to handle offsets while others have state in the kernel, for this reason you should assume that seeking only affects this package* Inputs: - io: The IO instance to seek on - fd: The file handle to seek - whence: The seek mode/where to seek from (default: Whence.Set) Returns: - new_offset: The offset that the file is at when the operation completed - err: The error when an error occured, 0 otherwise */ seek :: proc(io: ^IO, fd: os.Handle, offset: int, whence: Whence = .Set) -> (new_offset: int, err: os.Errno) { return _seek(io, fd, offset, whence) } /* The callback for non blocking `read` or `read_at` requests Inputs: - user: A passed through pointer from initiation to its callback - read: The amount of bytes that were read and added to the given buf - err: An error number if an error occured, 0 otherwise */ On_Read :: #type proc(user: rawptr, read: int, err: os.Errno) /* Reads from the given handle, at the handle's internal offset, at most `len(buf)` bytes, increases the file offset, and calls the given callback *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to read from - buf: The buffer to put read bytes into - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Read` for its arguments */ read :: proc(io: ^IO, fd: os.Handle, buf: []byte, user: rawptr, callback: On_Read) { _read(io, fd, nil, buf, user, callback) } /* Reads from the given handle, at the handle's internal offset, until the given buf is full or an error occurred, increases the file offset, and calls the given callback *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to read from - buf: The buffer to put read bytes into - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Read` for its arguments */ read_all :: proc(io: ^IO, fd: os.Handle, buf: []byte, user: rawptr, callback: On_Read) { _read(io, fd, nil, buf, user, callback, all = true) } /* Reads from the given handle, at the given offset, at most `len(buf)` bytes, and calls the given callback *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to read from - offset: The offset to begin the read from - buf: The buffer to put read bytes into - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Read` for its arguments */ read_at :: proc(io: ^IO, fd: os.Handle, offset: int, buf: []byte, user: rawptr, callback: On_Read) { _read(io, fd, offset, buf, user, callback) } /* Reads from the given handle, at the given offset, until the given buf is full or an error occurred, and calls the given callback *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to read from - offset: The offset to begin the read from - buf: The buffer to put read bytes into - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Read` for its arguments */ read_at_all :: proc(io: ^IO, fd: os.Handle, offset: int, buf: []byte, user: rawptr, callback: On_Read) { _read(io, fd, offset, buf, user, callback, all = true) } read_entire_file :: read_full /* Reads the entire file (size found by seeking to the end) into a singly allocated buffer that is returned. The callback is called once the file is read into the returned buf. *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to read from - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Read` for its arguments Returns: - buf: The buffer allocated to the size retrieved by seeking to the end of the file that is filled before calling the callback */ read_full :: proc(io: ^IO, fd: os.Handle, user: rawptr, callback: On_Read, allocator := context.allocator) -> []byte { size, err := seek(io, fd, 0, .End) if err != os.ERROR_NONE { callback(user, 0, err) return nil } if size <= 0 { callback(user, 0, os.ERROR_NONE) return nil } buf := make([]byte, size, allocator) read_at_all(io, fd, 0, buf, user, callback) return buf } /* The callback for non blocking `write`, `write_all`, `write_at` and `write_at_all` requests Inputs: - user: A passed through pointer from initiation to its callback - written: The amount of bytes that were written to the file - err: An error number if an error occured, 0 otherwise */ On_Write :: #type proc(user: rawptr, written: int, err: os.Errno) /* Writes to the given handle, at the handle's internal offset, at most `len(buf)` bytes, increases the file offset, and calls the given callback *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to write to - buf: The buffer to write to the file - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Write` for its arguments */ write :: proc(io: ^IO, fd: os.Handle, buf: []byte, user: rawptr, callback: On_Write) { _write(io, fd, nil, buf, user, callback) } /* Writes the given buffer to the given handle, at the handle's internal offset, increases the file offset, and calls the given callback This keeps writing until either an error or the full buffer being written *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to write to - buf: The buffer to write to the file - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Write` for its arguments */ write_all :: proc(io: ^IO, fd: os.Handle, buf: []byte, user: rawptr, callback: On_Write) { _write(io, fd, nil, buf, user, callback, true) } /* Writes to the given handle, at the given offset, at most `len(buf)` bytes, and calls the given callback *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to write to from - offset: The offset to begin the write from - buf: The buffer to write to the file - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Write` for its arguments */ write_at :: proc(io: ^IO, fd: os.Handle, offset: int, buf: []byte, user: rawptr, callback: On_Write) { _write(io, fd, offset, buf, user, callback) } /* Writes the given buffer to the given handle, at the given offset, and calls the given callback This keeps writing until either an error or the full buffer being written *Due to platform limitations, you must pass a `os.Handle` that was opened/returned using/by this package* Inputs: - io: The IO instance to use - fd: The file handle (created using/by this package) to write to from - offset: The offset to begin the write from - buf: The buffer to write to the file - user: A pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Write` for its arguments */ write_at_all :: proc(io: ^IO, fd: os.Handle, offset: int, buf: []byte, user: rawptr, callback: On_Write) { _write(io, fd, offset, buf, user, callback, true) } Poll_Event :: enum { // The subject is ready to be read from. Read, // The subject is ready to be written to. Write, } /* The callback for poll requests Inputs: - user: A passed through pointer from initiation to its callback - event: The event that is ready to go */ On_Poll :: #type proc(user: rawptr, event: Poll_Event) /* Polls for the given event on the subject handle Inputs: - io: The IO instance to use - fd: The file descriptor to poll - event: Whether to call the callback when `fd` is ready to be read from, or be written to - multi: Keeps the poll after an event happens, calling the callback again for further events, remove poll with `poll_remove` - user: An optional pointer that will be passed through to the callback, free to use by you and untouched by us - callback: The callback that is called when the operation completes, see docs for `On_Poll` for its arguments */ poll :: proc(io: ^IO, fd: os.Handle, event: Poll_Event, multi: bool, user: rawptr, callback: On_Poll) { _poll(io, fd, event, multi, user, callback) } /* Removes the polling for this `subject`+`event` pairing This is only needed when `poll` was called with `multi` set to `true` Inputs: - io: The IO instance to use - fd: The file descriptor to remove the poll of - event: The event to remove the poll of */ poll_remove :: proc(io: ^IO, fd: os.Handle, event: Poll_Event) { _poll_remove(io, fd, event) } MAX_USER_ARGUMENTS :: size_of(rawptr) * 5 Completion :: struct { // Implementation specifics, don't use outside of implementation/os. using _: _Completion, user_data: rawptr, // Callback pointer and user args passed in poly variants. user_args: [MAX_USER_ARGUMENTS + size_of(rawptr)]byte, } @(private) Operation :: union #no_nil { Op_Accept, Op_Close, Op_Connect, Op_Read, Op_Recv, Op_Send, Op_Write, Op_Timeout, Op_Next_Tick, Op_Poll, Op_Poll_Remove, }