Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

index.nct

//! Numeric Internet addresses for synchronous network I/O.
//!
//! Target-independent address values, TCP byte streams, UDP datagrams, and monotonic operation
//! timeouts share one ownership and error model.

use /fmt.Format
use /hash.Hash
use /internal/net.NetworkFailure
use /internal/net/model.NetworkAddress
use /io.{BlockingReader, BlockingWriter, Reader, TimedReader, TimedWriter, Writer}
use /mem.TryAllocator
use /order.TotalOrder
use /string.String
use /time.Duration
use /vec.Vec

see ./contracts.nct
see ./formatting.nct
see ./host_endpoint.nct
see ./parsing.nct
see ./resolution.nct
see ./resolution_tests.nct
see ./tests.nct
see ./tcp.nct
see ./async_tcp.nct
see ./errors.nct
see ./tcp_tests.nct
see ./provider_tests.nct
see ./udp.nct
see ./udp_tests.nct
see ./timeout_tests.nct
see ./values.nct

/// The Internet protocol address family represented by an `IpAddress`.
pub enum IpFamily {
    ipv4
    ipv6
}

/// One copyable 32-bit Internet Protocol version 4 address.
pub copy struct Ipv4Address

/// One copyable 128-bit Internet Protocol version 6 address.
pub copy struct Ipv6Address

/// One copyable numeric IPv4 or IPv6 address.
pub copy struct IpAddress

/// One copyable numeric IP address and logical transport port.
pub copy struct SocketAddress

/// One uniquely owned connected TCP byte stream.
pub struct TcpStream

/// One uniquely owned TCP listening socket.
pub struct TcpListener

/// One uniquely owned UDP datagram socket.
pub struct UdpSocket

/// Owned validated provider endpoint text used by package-internal asynchronous clients.
pub(/) struct HostEndpoint

/// Copies and validates one host and decimal service for asynchronous provider resolution.
pub(/) func host_endpoint(host: &str, port: u16): HostEndpoint!

instance HostEndpoint {
    /// Returns the retained NUL-terminated host text.
    pub(/) noalloc method &self.nul_host(): &str from self

    /// Returns the retained NUL-terminated decimal service text.
    pub(/) noalloc method &self.nul_service(): &str from self
}

/// Converts one public socket address to the target-independent provider representation.
pub(/) noalloc func substrate_address(address: &SocketAddress): NetworkAddress

/// Converts one provider address to its public value representation.
pub(/) noalloc func public_address(address: NetworkAddress): SocketAddress

/// Maps one provider-independent failure to the stable public network error vocabulary.
pub(/) noalloc func network_error(failure: NetworkFailure): error

/// The observable result of receiving exactly one UDP datagram.
pub copy struct DatagramRead

/// Connected TCP directions selected for local shutdown.
pub enum TcpShutdown {
    read
    write
    both
}

construct Ipv4Address {
    /// Creates an address from four octets in text order.
    pub noalloc func from_octets(first: u8, second: u8, third: u8, fourth: u8): Self

    /// Parses one complete dotted-decimal IPv4 spelling.
    pub noalloc func parse(text: &str): Self?

    /// Creates the unspecified address `0.0.0.0`.
    pub noalloc func unspecified(): Self

    /// Creates the loopback address `127.0.0.1`.
    pub noalloc func loopback(): Self
}

instance Ipv4Address {
    impl Format
    impl Hash
    impl TotalOrder

    /// Returns the four address octets in text order.
    pub noalloc method &self.octets(): [u8; 4]

    /// Reports whether this is the unspecified address.
    pub noalloc method &self.is_unspecified(): bool

    /// Reports whether this belongs to the `127.0.0.0/8` loopback range.
    pub noalloc method &self.is_loopback(): bool

    /// Returns the canonical dotted-decimal spelling.
    pub method self.to_string(): String

    /// Returns the canonical spelling using recoverable storage from `allocator`.
    pub method self.try_to_string(allocator: &+TryAllocator): String! from allocator

    /// Compares exact address octets.
    pub noalloc operator (&self == other: &Self): bool

    /// Orders exact address octets lexicographically.
    pub noalloc operator (&self < other: &Self): bool
}

construct Ipv6Address {
    /// Creates an address from eight 16-bit components in text order.
    pub noalloc func from_segments(
        first: u16,
        second: u16,
        third: u16,
        fourth: u16,
        fifth: u16,
        sixth: u16,
        seventh: u16,
        eighth: u16,
    ): Self

    /// Parses one complete numeric IPv6 spelling, including an optional embedded IPv4 tail.
    pub noalloc func parse(text: &str): Self?

    /// Creates the unspecified address `::`.
    pub noalloc func unspecified(): Self

    /// Creates the loopback address `::1`.
    pub noalloc func loopback(): Self
}

instance Ipv6Address {
    impl Format
    impl Hash
    impl TotalOrder

    /// Returns the eight 16-bit components in text order.
    pub noalloc method &self.segments(): [u16; 8]

    /// Reports whether this is the unspecified address.
    pub noalloc method &self.is_unspecified(): bool

    /// Reports whether this is the loopback address.
    pub noalloc method &self.is_loopback(): bool

    /// Returns the canonical RFC 5952 hexadecimal spelling.
    pub method self.to_string(): String

    /// Returns the canonical spelling using recoverable storage from `allocator`.
    pub method self.try_to_string(allocator: &+TryAllocator): String! from allocator

    /// Compares exact address octets.
    pub noalloc operator (&self == other: &Self): bool

    /// Orders exact address octets lexicographically.
    pub noalloc operator (&self < other: &Self): bool
}

construct IpAddress {
    /// Wraps one IPv4 address without changing its octets.
    pub noalloc func from_ipv4(address: Ipv4Address): Self

    /// Wraps one IPv6 address without changing its octets.
    pub noalloc func from_ipv6(address: Ipv6Address): Self

    /// Parses one complete numeric IPv4 or IPv6 spelling.
    pub noalloc func parse(text: &str): Self?
}

instance IpAddress {
    impl Format
    impl Hash
    impl TotalOrder

    /// Returns this address's explicit family.
    pub noalloc method &self.family(): IpFamily

    /// Returns the represented IPv4 address, or absence for IPv6.
    pub noalloc method &self.as_ipv4(): Ipv4Address?

    /// Returns the represented IPv6 address, or absence for IPv4.
    pub noalloc method &self.as_ipv6(): Ipv6Address?

    /// Returns the canonical numeric spelling.
    pub method self.to_string(): String

    /// Returns the canonical spelling using recoverable storage from `allocator`.
    pub method self.try_to_string(allocator: &+TryAllocator): String! from allocator

    /// Compares the family and exact address octets.
    pub noalloc operator (&self == other: &Self): bool

    /// Orders IPv4 before IPv6, then orders exact address octets lexicographically.
    pub noalloc operator (&self < other: &Self): bool
}

construct SocketAddress {
    /// Combines a numeric address with one logical transport port.
    pub noalloc func new(address: IpAddress, port: u16): Self

    /// Parses `IPv4:port` or `[IPv6]:port` from one complete input.
    pub noalloc func parse(text: &str): Self?
}

instance SocketAddress {
    impl Format
    impl Hash
    impl TotalOrder

    /// Returns the numeric IP address.
    pub noalloc method &self.ip(): IpAddress

    /// Returns the logical transport port.
    pub noalloc method &self.port(): u16

    /// Returns the canonical address and decimal port spelling.
    pub method self.to_string(): String

    /// Returns the canonical spelling using recoverable storage from `allocator`.
    pub method self.try_to_string(allocator: &+TryAllocator): String! from allocator

    /// Compares the logical IP address and port.
    pub noalloc operator (&self == other: &Self): bool

    /// Orders by logical IP address, then by port.
    pub noalloc operator (&self < other: &Self): bool
}

construct TcpStream {
    /// Connects synchronously to one numeric socket address.
    pub noalloc blocking func connect_blocking(address: SocketAddress): Self!

    /// Connects synchronously, returning `std.net.timed_out` when `timeout` expires.
    pub noalloc blocking func connect_with_timeout_blocking(
        address: SocketAddress,
        timeout: Duration,
    ): Self!

    /// Resolves `host` and connects to the first reachable address in system order.
    pub blocking func connect_host_blocking(host: &str, port: u16): Self!

    /// Resolves and connects without restarting `timeout` for each candidate.
    pub blocking func connect_host_with_timeout_blocking(
        host: &str,
        port: u16,
        timeout: Duration,
    ): Self!
}

/// Resolves one ASCII host into numeric socket addresses in system order.
pub blocking func resolve(host: &str, port: u16): Vec<SocketAddress>!

/// Connects to one numeric address without blocking the executor thread.
pub async func connect_tcp(address: SocketAddress): TcpStream!

/// Connects asynchronously until the fixed relative timeout expires.
pub async func connect_tcp_with_timeout(
    address: SocketAddress,
    timeout: Duration,
): TcpStream!

/// Validates, resolves, and connects one host while the future is driven.
pub async func connect_host(host: &str, port: u16): TcpStream!

/// Validates, resolves, and connects through the provider before one future-owned fixed timeout.
pub async func connect_host_with_timeout(
    host: &str,
    port: u16,
    timeout: Duration,
): TcpStream!

/// Resolves using recoverable result storage from `allocator`.
pub blocking func try_resolve(
    allocator: &+TryAllocator,
    host: &str,
    port: u16,
): Vec<SocketAddress>! from allocator

instance TcpStream {
    impl BlockingReader
    impl BlockingWriter
    impl Reader
    impl TimedReader
    impl TimedWriter
    impl Writer

    /// Returns the effective local socket address.
    pub noalloc method &self.local_address(): SocketAddress!

    /// Returns the connected peer socket address.
    pub noalloc method &self.peer_address(): SocketAddress!

    /// Replaces the read timeout; absence permits an unlimited wait.
    pub noalloc method &+self.set_read_timeout(timeout: Duration?): void

    /// Returns the configured read timeout.
    pub noalloc method &self.read_timeout(): Duration?

    /// Replaces the write timeout; absence permits an unlimited wait.
    pub noalloc method &+self.set_write_timeout(timeout: Duration?): void

    /// Returns the configured write timeout.
    pub noalloc method &self.write_timeout(): Duration?

    /// Shuts down selected local stream directions without closing the socket.
    pub noalloc blocking method &+self.shutdown(direction: TcpShutdown): void!

    /// Makes the stream terminal and releases its descriptor at most once.
    pub noalloc method &+self.close(): void

    /// Reads once without blocking the executor thread.
    pub async method &+self.read(buffer: &+[u8]): usize!

    /// Reads once asynchronously until the fixed relative timeout expires.
    pub async method &+self.read_with_timeout(
        buffer: &+[u8],
        timeout: Duration,
    ): usize!

    /// Writes the complete byte view without blocking the executor thread.
    pub async method &+self.write(bytes: &[u8]): void!

    /// Writes asynchronously until the complete view is sent or the fixed timeout expires.
    pub async method &+self.write_with_timeout(
        bytes: &[u8],
        timeout: Duration,
    ): void!
}

construct TcpListener {
    /// Binds one numeric address and starts listening with the platform's standard backlog.
    pub noalloc blocking func bind_blocking(address: SocketAddress): Self!
}

/// Binds one numeric TCP listener without blocking the executor thread.
pub async func bind_tcp(address: SocketAddress): TcpListener!

instance TcpListener {
    /// Returns the effective local address, including a kernel-selected port.
    pub noalloc method &self.local_address(): SocketAddress!

    /// Accepts one connected stream and returns its peer address.
    pub noalloc blocking method &+self.accept_blocking(): (TcpStream, SocketAddress)!

    /// Accepts one connection without blocking the executor thread.
    pub async method &+self.accept(): (TcpStream, SocketAddress)!

    /// Accepts asynchronously until the fixed relative timeout expires.
    pub async method &+self.accept_with_timeout(
        timeout: Duration,
    ): (TcpStream, SocketAddress)!

    /// Replaces the accept timeout; absence permits an unlimited wait.
    pub noalloc method &+self.set_accept_timeout(timeout: Duration?): void

    /// Returns the configured accept timeout.
    pub noalloc method &self.accept_timeout(): Duration?

    /// Makes the listener terminal and releases its descriptor at most once.
    pub noalloc method &+self.close(): void
}

construct UdpSocket {
    /// Binds one numeric local address for datagram communication.
    pub noalloc func bind(address: SocketAddress): Self!
}

instance UdpSocket {
    /// Connects this socket to one peer for subsequent `send` operations.
    pub noalloc method &+self.connect(peer: SocketAddress): void!

    /// Sends one complete datagram to the connected peer without blocking the executor thread.
    pub async method &+self.send(bytes: &[u8]): void!

    /// Sends to the connected peer until one fixed relative timeout expires.
    pub async method &+self.send_with_timeout(bytes: &[u8], timeout: Duration): void!

    /// Sends one complete datagram to an explicit peer without blocking the executor thread.
    pub async method &+self.send_to(bytes: &[u8], target: SocketAddress): void!

    /// Sends to an explicit peer until one fixed relative timeout expires.
    pub async method &+self.send_to_with_timeout(
        bytes: &[u8],
        target: SocketAddress,
        timeout: Duration,
    ): void!

    /// Receives one datagram without blocking the executor thread.
    pub async method &+self.receive(buffer: &+[u8]): DatagramRead!

    /// Receives one datagram until one fixed relative timeout expires.
    pub async method &+self.receive_with_timeout(
        buffer: &+[u8],
        timeout: Duration,
    ): DatagramRead!

    /// Sends one complete datagram synchronously using the configured write timeout.
    pub noalloc blocking method &+self.send_blocking(bytes: &[u8]): void!

    /// Sends one complete addressed datagram synchronously using the configured write timeout.
    pub noalloc blocking method &+self.send_to_blocking(
        bytes: &[u8],
        target: SocketAddress,
    ): void!

    /// Receives one datagram synchronously using the configured read timeout.
    pub noalloc blocking method &+self.receive_blocking(buffer: &+[u8]): DatagramRead!

    /// Returns the effective local address, including a kernel-selected port.
    pub noalloc method &self.local_address(): SocketAddress!

    /// Returns the connected peer address or an error when no peer is connected.
    pub noalloc method &self.peer_address(): SocketAddress!

    /// Replaces the receive timeout; absence permits an unlimited wait.
    pub noalloc method &+self.set_read_timeout(timeout: Duration?): void

    /// Returns the configured receive timeout.
    pub noalloc method &self.read_timeout(): Duration?

    /// Replaces the blocking-send timeout; absence permits an unlimited wait.
    pub noalloc method &+self.set_write_timeout(timeout: Duration?): void

    /// Returns the configured blocking-send timeout.
    pub noalloc method &self.write_timeout(): Duration?

    /// Makes the socket terminal and releases its descriptor at most once.
    pub noalloc method &+self.close(): void
}

instance DatagramRead {
    /// Returns the sender of the consumed datagram.
    pub noalloc method &self.source(): SocketAddress

    /// Returns how many bytes were copied into the supplied buffer.
    pub noalloc method &self.copied_len(): usize

    /// Reports whether the consumed datagram exceeded the supplied buffer.
    pub noalloc method &self.was_truncated(): bool
}