pub struct PinnedBuffer<T: ?Sized> { /* private fields */ }Expand description
A buffer that is pinned in memory, primarily for educational purposes.
§⚠️ FUNDAMENTALLY LIMITED - DO NOT USE FOR I/O OPERATIONS
This API is considered educational and is not suitable for practical applications involving I/O.
It suffers from fundamental lifetime constraints in Rust that make it impossible to use in loops
or for multiple concurrent operations on the same Ring instance. It exists to
demonstrate the complexities that the OwnedBuffer model successfully solves.
For all applications, you MUST use OwnedBuffer with the *_owned methods on Ring.
§The Core Problem
The Ring methods that accept PinnedBuffer (e.g., ring.read()) return a Future that
holds a mutable borrow on both the Ring and the buffer for their entire lifetimes. This
makes it impossible for the borrow checker to allow a second operation in a loop or
concurrently, as the first borrow is never released.
use safer_ring::{Ring, PinnedBuffer};
let mut ring = Ring::new(32)?;
let mut buffer = PinnedBuffer::with_capacity(1024);
// This fails to compile due to lifetime constraints:
for _ in 0..2 {
let (_, buf) = ring.read(0, buffer.as_mut_slice())?.await?;
buffer = buf; // Error: cannot use ring again while borrowed
}§When is PinnedBuffer useful?
- Benchmarking allocation strategies (e.g.,
with_capacity_aligned,with_capacity_numa). - Situations where you need a single, one-shot I/O operation and the buffer and ring will be dropped immediately after.
- As a building block for more complex,
unsafeabstractions.
For all other cases, and especially for application-level code, use OwnedBuffer.
§Memory Layout
The buffer uses heap allocation via Pin<Box<T>> which guarantees:
- Stable memory addresses (required for io_uring)
- Automatic cleanup when dropped
- Zero-copy semantics for I/O operations
§Generation Tracking
Each buffer includes a GenerationCounter for lifecycle tracking and debugging.
This helps identify buffer reuse patterns and can assist in detecting potential
use-after-free scenarios during development.
§Examples
Valid use cases (allocation benchmarking):
use safer_ring::buffer::PinnedBuffer;
use std::pin::Pin;
// Benchmarking different allocation strategies
let standard_buffer = PinnedBuffer::with_capacity(4096);
let aligned_buffer = PinnedBuffer::with_capacity_aligned(4096);
let numa_buffer = PinnedBuffer::with_capacity_numa(4096, Some(0));
// Single, one-shot operation (not practical for real apps)
let buffer = PinnedBuffer::new([1, 2, 3, 4]);
let pinned_ref: Pin<&[u8; 4]> = buffer.as_pin();Implementations§
Source§impl<T: ?Sized> PinnedBuffer<T>
impl<T: ?Sized> PinnedBuffer<T>
Sourcepub fn as_pin(&self) -> Pin<&T>
pub fn as_pin(&self) -> Pin<&T>
Returns a pinned reference to the buffer data.
This method provides safe access to the pinned data while maintaining the pinning guarantees required for io_uring operations.
§Examples
use safer_ring::buffer::PinnedBuffer;
use std::pin::Pin;
let buffer = PinnedBuffer::new([1, 2, 3, 4]);
let pinned_ref: Pin<&[u8; 4]> = buffer.as_pin();
assert_eq!(&*pinned_ref, &[1, 2, 3, 4]);Sourcepub fn as_pin_mut(&mut self) -> Pin<&mut T>
pub fn as_pin_mut(&mut self) -> Pin<&mut T>
Returns a mutable pinned reference to the buffer data.
This method provides safe mutable access to the pinned data while maintaining the pinning guarantees. Essential for io_uring write operations.
§Examples
use safer_ring::buffer::PinnedBuffer;
use std::pin::Pin;
let mut buffer = PinnedBuffer::new([0; 4]);
let mut pinned_ref: Pin<&mut [u8; 4]> = buffer.as_pin_mut();
// Safe to modify through pinned referenceSourcepub fn generation(&self) -> u64
pub fn generation(&self) -> u64
Returns the current generation of this buffer.
The generation counter tracks buffer lifecycle events and can be used for debugging buffer reuse patterns and detecting potential issues.
§Examples
use safer_ring::buffer::PinnedBuffer;
let mut buffer = PinnedBuffer::with_capacity(1024);
let initial_gen = buffer.generation();
buffer.mark_in_use();
assert!(buffer.generation() > initial_gen);Sourcepub fn mark_in_use(&mut self)
pub fn mark_in_use(&mut self)
Mark this buffer as in use and increment generation.
This method should be called when the buffer is being used for I/O operations. It helps track buffer lifecycle for debugging purposes.
§Examples
use safer_ring::buffer::PinnedBuffer;
let mut buffer = PinnedBuffer::with_capacity(1024);
let gen_before = buffer.generation();
buffer.mark_in_use();
assert_eq!(buffer.generation(), gen_before + 1);Sourcepub fn mark_available(&mut self)
pub fn mark_available(&mut self)
Mark this buffer as available and increment generation.
This method should be called when the buffer is no longer being used for I/O operations and is available for reuse.
§Examples
use safer_ring::buffer::PinnedBuffer;
let mut buffer = PinnedBuffer::with_capacity(1024);
buffer.mark_in_use();
let gen_after_use = buffer.generation();
buffer.mark_available();
assert_eq!(buffer.generation(), gen_after_use + 1);Sourcepub fn is_available(&self) -> bool
pub fn is_available(&self) -> bool
Check if this buffer is available for use.
Note: This is a simple implementation - a more sophisticated version might track actual usage state.
Sourcepub fn as_ptr(&self) -> *const T
pub fn as_ptr(&self) -> *const T
Returns a raw pointer to the buffer data.
§Safety
The pointer is valid only while the buffer exists.
Sourcepub fn as_mut_ptr(&mut self) -> *mut T
pub fn as_mut_ptr(&mut self) -> *mut T
Returns a mutable raw pointer to the buffer data.
§Safety
The pointer is valid only while the buffer exists.
Source§impl<T> PinnedBuffer<T>
impl<T> PinnedBuffer<T>
Sourcepub fn new(data: T) -> Self
pub fn new(data: T) -> Self
Creates a new pinned buffer from the given data.
This constructor takes ownership of the provided data and pins it in memory, making it suitable for io_uring operations. The data is moved to the heap and its address becomes stable for the lifetime of the buffer.
§Parameters
data- The data to pin in memory. Can be any type T.
§Returns
Returns a new PinnedBuffer<T> with the data pinned and generation counter
initialized to 0.
§Examples
use safer_ring::buffer::PinnedBuffer;
// Pin an array
let buffer = PinnedBuffer::new([1, 2, 3, 4]);
assert_eq!(buffer.len(), 4);
// Pin a custom struct
#[derive(Debug, PartialEq)]
struct Data { value: u32 }
let buffer = PinnedBuffer::new(Data { value: 42 });
assert_eq!(buffer.as_pin().value, 42);Source§impl PinnedBuffer<[u8]>
impl PinnedBuffer<[u8]>
Sourcepub fn with_capacity(size: usize) -> Self
pub fn with_capacity(size: usize) -> Self
Creates a new zero-initialized pinned buffer with the specified size.
This is the primary method for creating buffers for I/O operations. The buffer is heap-allocated, zero-initialized, and pinned for stable memory addresses required by io_uring.
§Parameters
size- The size of the buffer in bytes. Must be greater than 0 for meaningful use.
§Returns
Returns a PinnedBuffer<[u8]> containing a zero-initialized buffer of the
specified size, ready for I/O operations.
§Examples
use safer_ring::buffer::PinnedBuffer;
// Create a 4KB buffer for file I/O
let buffer = PinnedBuffer::with_capacity(4096);
assert_eq!(buffer.len(), 4096);
assert!(buffer.as_slice().iter().all(|&b| b == 0)); // All zeros
// Create buffer for network I/O
let net_buffer = PinnedBuffer::with_capacity(1500); // MTU size
assert_eq!(net_buffer.len(), 1500);Sourcepub fn from_vec(vec: Vec<u8>) -> Self
pub fn from_vec(vec: Vec<u8>) -> Self
Creates a new pinned buffer from a vector.
This method takes ownership of a vector and converts it into a pinned buffer. The vector’s data is preserved and the buffer can be used immediately for I/O operations.
§Parameters
vec- The vector to convert into a pinned buffer.
§Returns
Returns a PinnedBuffer<[u8]> containing the vector’s data, pinned
and ready for I/O operations.
§Examples
use safer_ring::buffer::PinnedBuffer;
let data = vec![1, 2, 3, 4, 5];
let buffer = PinnedBuffer::from_vec(data);
assert_eq!(buffer.as_slice(), &[1, 2, 3, 4, 5]);
assert_eq!(buffer.len(), 5);Sourcepub fn from_boxed_slice(slice: Box<[u8]>) -> Self
pub fn from_boxed_slice(slice: Box<[u8]>) -> Self
Creates a new pinned buffer from a boxed slice.
Sourcepub fn from_slice(slice: &[u8]) -> Self
pub fn from_slice(slice: &[u8]) -> Self
Creates a new pinned buffer by copying from a slice.
Sourcepub fn with_capacity_aligned(size: usize) -> Self
pub fn with_capacity_aligned(size: usize) -> Self
Creates a new aligned pinned buffer with the specified size.
This method creates a pinned buffer using page-aligned allocation (4096 bytes) for optimal DMA performance with io_uring operations. The alignment helps reduce memory copy overhead in the kernel.
§Parameters
size- The size of the buffer in bytes. The buffer will be page-aligned regardless of the size specified.
§Returns
Returns a PinnedBuffer<[u8]> with page-aligned, zero-initialized memory
optimized for high-performance I/O operations.
§Performance Notes
Page-aligned buffers can provide significant performance benefits for:
- Large sequential I/O operations
- Direct memory access (DMA) operations
- Kernel bypass operations with io_uring
§Examples
use safer_ring::buffer::PinnedBuffer;
// Create aligned buffer for high-performance I/O
let buffer = PinnedBuffer::with_capacity_aligned(8192);
assert_eq!(buffer.len(), 8192);
assert!(buffer.as_slice().iter().all(|&b| b == 0)); // Zero-initialized
// Even small sizes get page alignment benefits
let small_aligned = PinnedBuffer::with_capacity_aligned(64);
assert_eq!(small_aligned.len(), 64);Sourcepub fn with_capacity_numa(size: usize, numa_node: Option<usize>) -> Self
pub fn with_capacity_numa(size: usize, numa_node: Option<usize>) -> Self
Creates a new NUMA-aware pinned buffer with the specified size. On Linux, attempts to allocate memory on the specified NUMA node.
Sourcepub fn as_mut_slice(&mut self) -> Pin<&mut [u8]>
pub fn as_mut_slice(&mut self) -> Pin<&mut [u8]>
Returns a mutable slice reference with pinning guarantees.
Source§impl<const N: usize> PinnedBuffer<[u8; N]>
impl<const N: usize> PinnedBuffer<[u8; N]>
Sourcepub fn from_array(array: [u8; N]) -> Self
pub fn from_array(array: [u8; N]) -> Self
Creates a new pinned buffer from a fixed-size array.
Sourcepub fn as_mut_slice(&mut self) -> Pin<&mut [u8]>
pub fn as_mut_slice(&mut self) -> Pin<&mut [u8]>
Returns a mutable slice reference with pinning guarantees.