/*
 *
 * Copyright 2026 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Package readyreader provides utilities to perform non-memory-pinning reads.
package readyreader import ( ) // Reader is an optional interface that can be implemented by [net.Conn] // implementations to enable gRPC to perform non-memory-pinning reads. type Reader interface { // ReadOnReady waits for data to arrive, fetches a buffer, and performs a // read. When the underlying IO is readable, it allocates a buffer of size // bufSize from the pool and reads up to bufSize bytes into the buffer. // // It returns a pointer to the buffer so it can be returned to the pool // later, the number of bytes read, and an error. // // Callers should always process the n > 0 bytes returned before considering // the error. Doing so correctly handles I/O errors that happen after // reading some bytes, as well as both of the allowed EOF behaviors. ReadOnReady(bufSize int, pool mem.BufferPool) (b *[]byte, n int, err error) } // nonBlockingReader is optimized for non-memory-pinning reads using the RawConn // interface. type nonBlockingReader struct { raw syscall.RawConn // The following fields are stored as field to avoid heap allocations. state readState doRead func(fd uintptr) bool } type readState struct { // Request params. bufSize int pool mem.BufferPool // Response params. readError error bytesRead int buf *[]byte } // NewNonBlocking returns a ReadyReader if the passed reader supports // non-memory-pinning reads, else nil. func ( io.Reader) Reader { if , := .(Reader); { return } if !isRawConnSupported() { return nil } // We restrict the types before asserting syscall.Conn. The credentials // package may return a wrapper that implements syscall.Conn by embedding // both the raw connection and the encrypted connection. If the code // attempts to read directly from the raw syscall.RawConn, it would read // encrypted data. switch .(type) { case *net.TCPConn, *net.UDPConn, *net.UnixConn, *net.IPConn: default: return nil } , := .(syscall.Conn) if ! { return nil } , := .SyscallConn() if != nil { return nil } := &nonBlockingReader{raw: } .doRead = func( uintptr) bool { := &.state .buf = .pool.Get(.bufSize) .bytesRead, .readError = sysRead(, *.buf) if .readError != nil { .pool.Put(.buf) .buf = nil } return !wouldBlock(.readError) } return } func ( *nonBlockingReader) ( int, mem.BufferPool) (*[]byte, int, error) { .state = readState{ pool: , bufSize: , } := .raw.Read(.doRead) := .state.buf := .state.bytesRead := .state.readError .state = readState{} if != nil { if != nil { .Put() } return nil, 0, } if != nil { // buffer is already released in the callback. return nil, 0, } if == 0 { // syscall.Read doesn't consider a graceful socket closure to be an // error condition, but Go's io.Reader expects an EOF error. .Put() return nil, 0, io.EOF } return , , nil } type blockingReader struct { reader io.Reader } func ( *blockingReader) ( int, mem.BufferPool) (*[]byte, int, error) { := .Get() , := .reader.Read(*) if != nil { .Put() return nil, 0, } return , , nil } // New detects if [syscall.RawConn] is available for non-memory-pinning reads. // If [syscall.RawConn] is unavailable, it falls back to using the simpler // [io.Reader] interface for reads. func ( io.Reader) Reader { if := NewNonBlocking(); != nil { return } return &blockingReader{reader: } } // bufReadyReader implements buffering for a ReadyReader object. // A new bufReadyReader is created by calling [NewBuffered]. type bufReadyReader struct { buf *[]byte pool mem.BufferPool bufSize int rd Reader // reader provided by the caller r, w int // buf read and write positions err error constPool constBufferPool // stored as a field to avoid heap allocations. } // NewBuffered returns a new [io.Reader] with a buffer of the specified size // which is allocated from the provided pool. func ( Reader, int, mem.BufferPool) io.Reader { return &bufReadyReader{ rd: , pool: , bufSize: , } } func ( *bufReadyReader) () error { := .err .err = nil return } func ( *bufReadyReader) () int { return .w - .r } // Read reads data into p. It returns the number of bytes read into p. The // bytes are taken from at most one Read on the underlying [ReadyReader], // hence n may be less than len(p). If the underlying [ReadyReader] can return // a non-zero count with io.EOF, then this Read method can do so as well; see // the [io.Reader] docs. func ( *bufReadyReader) ( []byte) ( int, error) { = len() if == 0 { if .buffered() > 0 { return 0, nil } return 0, .readErr() } if .r == .w { if .err != nil { return 0, .readErr() } if len() >= .bufSize { // Large read, empty buffer. // Read directly into p to avoid copy. .constPool.buffer = _, , .err = .rd.ReadOnReady(len(), &.constPool) return , .readErr() } // One read. .r = 0 .w = 0 .buf, , .err = .rd.ReadOnReady(.bufSize, .pool) if == 0 { if .buf != nil { .pool.Put(.buf) .buf = nil } return 0, .readErr() } .w += } // copy as much as we can // b.buf must be non-nil since b.r != b.w. := *.buf = copy(, [.r:.w]) .r += if .r == .w { // Consumed entire buffer, release it. .pool.Put(.buf) .buf = nil } return , nil } type constBufferPool struct { buffer []byte } func ( *constBufferPool) (int) *[]byte { return &.buffer } func ( *constBufferPool) (*[]byte) {}