# irpc: Serialization and Utility Modules ## Varint Utilities The `varint-util` module (available with `rpc` or `varint-util` feature) provides LEB128 varint encoding/decoding compatible with postcard's format. ### Async Reading ```rust pub async fn read_varint_u64(reader: &mut R) -> io::Result> ``` Reads a LEB128-encoded `u64` from an async reader. Returns `Ok(None)` on `UnexpectedEof` at the first byte position (clean stream end). **Format:** Each byte uses 7 bits for the value, MSB as continuation bit. Values stored little-endian (least significant group first). ### Sync Writing ```rust pub fn write_varint_u64_sync(writer: &mut W, value: u64) -> io::Result ``` Writes a `u64` as LEB128 to a synchronous writer. ### Length-Prefixed Encoding ```rust // Sync: pub fn write_length_prefixed(write: impl io::Write, value: T) -> io::Result<()> pub trait WriteVarintExt: io::Write { fn write_varint_u64(&mut self, value: u64) -> io::Result; fn write_length_prefixed(&mut self, value: T) -> io::Result<()>; } // Async: pub trait AsyncReadVarintExt: AsyncRead + Unpin { fn read_varint_u64(&mut self) -> impl Future>>; fn read_length_prefixed(&mut self, max_size: usize) -> impl Future>; } pub trait AsyncWriteVarintExt: AsyncWrite + Unpin { fn write_varint_u64(&mut self, value: u64) -> impl Future>; fn write_length_prefixed(&mut self, value: V) -> impl Future>; } ``` The length-prefix format is: ``` [varint-encoded-length][postcard-serialized-data] ``` Used internally by irpc for framing all messages on QUIC streams. The `max_size` parameter in `read_length_prefixed` prevents memory exhaustion from malicious length values. ## noq Endpoint Setup The `noq_endpoint_setup` feature provides helpers for creating noq endpoints with TLS configuration: ```rust pub fn configure_client(server_certs: &[&[u8]]) -> Result pub fn configure_server() -> Result<(ServerConfig, Vec)> pub fn configure_client_insecure() -> Result // Non-WASM only: pub fn make_client_endpoint(bind_addr: SocketAddr, server_certs: &[&[u8]]) -> Result pub fn make_insecure_client_endpoint(bind_addr: SocketAddr) -> Result pub fn make_server_endpoint(bind_addr: SocketAddr) -> Result<(Endpoint, Vec)> ``` - `configure_server()`: Creates a self-signed certificate with rcgen and configures the server with TLS 1.3. Returns the DER-encoded certificate for clients to trust. - `configure_client()`: Configures a client to trust specific DER certificates. - `configure_client_insecure()`: Skips certificate verification (for testing only). - Server endpoints set `max_concurrent_uni_streams(0)` to disable unidirectional streams (only bidirectional streams are used). - Keep-alive interval is set to 1 second on client configs. ## FusedOneshotReceiver ```rust pub(crate) struct FusedOneshotReceiver(pub tokio::sync::oneshot::Receiver); ``` A wrapper that prevents panics when polling an already-completed oneshot receiver. After the inner receiver resolves, subsequent polls return `Poll::Pending` indefinitely instead of panicking. This is important because irpc's `oneshot::Receiver` can be wrapped in `Receiver::Boxed` (a `BoxFuture`), and the inner future might be polled multiple times in certain select patterns. ## now_or_never ```rust pub(crate) fn now_or_never(future: F) -> Option ``` Attempts to complete a future immediately without blocking. If the future would block, returns `None`. Used internally by `NoqSenderInner::try_send()` to attempt an immediate write to the QUIC stream without yielding. Implementation uses a no-op waker to poll the future once. ## Spans Feature When the `spans` feature is enabled (default), `WithChannels` includes a `span: tracing::Span` field: ```rust pub struct WithChannels, S: Service> { pub inner: I, pub tx: >::Tx, pub rx: >::Rx, #[cfg(feature = "spans")] pub span: tracing::Span, } ``` The span is captured from `tracing::Span::current()` at the time of `WithChannels` construction (via `From` implementations). This preserves tracing context across async message-passing boundaries. The `rpc_requests` macro generates a `parent_span()` method on the message enum when `no_spans` is not set: ```rust impl ComputeMessage { pub fn parent_span(&self) -> tracing::Span { let span = match self { ComputeMessage::Multiply(inner) => inner.parent_span_opt(), ComputeMessage::Sum(inner) => inner.parent_span_opt(), }; span.cloned().unwrap_or_else(|| tracing::Span::current()) } } ``` This allows server-side handlers to enter the client's tracing span: ```rust async fn handle(msg: ComputeMessage) { let _entered = msg.parent_span().enter(); // ... processing happens in the client's tracing context } ``` When `no_spans` is set in the macro, no span-related code is generated, making it compatible with builds that don't have the `spans` feature enabled.