safer_ring/operation/
building.rs

1//! Building state implementation and operation constructors.
2//!
3//! This module contains all the functionality for operations in the Building state,
4//! including constructors and configuration methods.
5
6use std::marker::PhantomData;
7use std::os::unix::io::RawFd;
8use std::pin::Pin;
9
10use super::core::{BufferType, FdType, Operation};
11use crate::operation::{Building, OperationType, Submitted};
12use crate::registry::{RegisteredBuffer, RegisteredFd};
13
14impl<'ring, 'buf> Default for Operation<'ring, 'buf, Building> {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl<'ring, 'buf> Operation<'ring, 'buf, Building> {
21    /// Create a new operation in the building state.
22    ///
23    /// The operation starts with default values and must be configured
24    /// before it can be submitted.
25    ///
26    /// # Example
27    ///
28    /// ```rust,no_run
29    /// # use safer_ring::operation::Operation;
30    /// let op = Operation::new()
31    ///     .fd(0)
32    ///     .offset(1024);
33    /// ```
34    pub fn new() -> Self {
35        Self::with_type(OperationType::Read) // Default to read for backwards compatibility
36    }
37
38    /// Create a new read operation.
39    ///
40    /// # Example
41    ///
42    /// ```rust,no_run
43    /// # use safer_ring::operation::Operation;
44    /// # use std::pin::Pin;
45    /// let mut buffer = vec![0u8; 1024];
46    /// let op = Operation::read()
47    ///     .fd(0)
48    ///     .buffer(Pin::new(buffer.as_mut_slice()));
49    /// ```
50    #[inline]
51    pub fn read() -> Self {
52        Self::with_type(OperationType::Read)
53    }
54
55    /// Create a new vectored read operation.
56    ///
57    /// # Example
58    ///
59    /// ```rust,no_run
60    /// # use safer_ring::operation::Operation;
61    /// # use std::pin::Pin;
62    /// let mut buffer1 = vec![0u8; 512];
63    /// let mut buffer2 = vec![0u8; 512];
64    /// let buffers = vec![
65    ///     Pin::new(buffer1.as_mut_slice()),
66    ///     Pin::new(buffer2.as_mut_slice()),
67    /// ];
68    /// let op = Operation::read_vectored()
69    ///     .fd(0)
70    ///     .buffers(buffers);
71    /// ```
72    #[inline]
73    pub fn read_vectored() -> Self {
74        Self::with_type(OperationType::ReadVectored)
75    }
76
77    /// Create a new write operation.
78    ///
79    /// # Example
80    ///
81    /// ```rust,no_run
82    /// # use safer_ring::operation::Operation;
83    /// # use std::pin::Pin;
84    /// let mut buffer = b"Hello, world!".to_vec();
85    /// let op = Operation::write()
86    ///     .fd(1)
87    ///     .buffer(Pin::new(buffer.as_mut_slice()));
88    /// ```
89    #[inline]
90    pub fn write() -> Self {
91        Self::with_type(OperationType::Write)
92    }
93
94    /// Create a new vectored write operation.
95    ///
96    /// # Example
97    ///
98    /// ```rust,no_run
99    /// # use safer_ring::operation::Operation;
100    /// # use std::pin::Pin;
101    /// let mut buffer1 = b"Hello, ".to_vec();
102    /// let mut buffer2 = b"world!".to_vec();
103    /// let buffers = vec![
104    ///     Pin::new(buffer1.as_mut_slice()),
105    ///     Pin::new(buffer2.as_mut_slice()),
106    /// ];
107    /// let op = Operation::write_vectored()
108    ///     .fd(1)
109    ///     .buffers(buffers);
110    /// ```
111    #[inline]
112    pub fn write_vectored() -> Self {
113        Self::with_type(OperationType::WriteVectored)
114    }
115
116    /// Create a new accept operation.
117    ///
118    /// # Example
119    ///
120    /// ```rust,no_run
121    /// # use safer_ring::operation::Operation;
122    /// let op = Operation::accept().fd(3); // listening socket fd
123    /// ```
124    #[inline]
125    pub fn accept() -> Self {
126        Self::with_type(OperationType::Accept)
127    }
128
129    /// Create a new send operation.
130    ///
131    /// # Example
132    ///
133    /// ```rust,no_run
134    /// # use safer_ring::operation::Operation;
135    /// # use std::pin::Pin;
136    /// let mut buffer = b"Hello, client!".to_vec();
137    /// let op = Operation::send()
138    ///     .fd(4)
139    ///     .buffer(Pin::new(buffer.as_mut_slice()));
140    /// ```
141    #[inline]
142    pub fn send() -> Self {
143        Self::with_type(OperationType::Send)
144    }
145
146    /// Create a new receive operation.
147    ///
148    /// # Example
149    ///
150    /// ```rust,no_run
151    /// # use safer_ring::operation::Operation;
152    /// # use std::pin::Pin;
153    /// let mut buffer = vec![0u8; 1024];
154    /// let op = Operation::recv()
155    ///     .fd(4)
156    ///     .buffer(Pin::new(buffer.as_mut_slice()));
157    /// ```
158    #[inline]
159    pub fn recv() -> Self {
160        Self::with_type(OperationType::Recv)
161    }
162
163    /// Create a new operation with the specified type.
164    ///
165    /// This is a helper method to reduce code duplication in the constructor methods.
166    #[inline]
167    fn with_type(op_type: OperationType) -> Self {
168        Self {
169            ring: PhantomData,
170            buffer: BufferType::None,
171            fd: FdType::Raw(-1), // Invalid fd that must be set before submission
172            offset: 0,
173            op_type,
174            state: Building,
175        }
176    }
177
178    /// Set the file descriptor for this operation.
179    ///
180    /// This is required for all operations and must be a valid file descriptor.
181    ///
182    /// # Arguments
183    ///
184    /// * `fd` - A valid file descriptor (>= 0)
185    ///
186    /// # Example
187    ///
188    /// ```rust,no_run
189    /// # use safer_ring::operation::Operation;
190    /// let op = Operation::read().fd(0); // stdin
191    /// ```
192    #[inline]
193    pub fn fd(mut self, fd: RawFd) -> Self {
194        self.fd = FdType::Raw(fd);
195        self
196    }
197
198    /// Set a registered file descriptor for this operation.
199    ///
200    /// Using registered file descriptors can improve performance for frequently
201    /// used file descriptors by avoiding kernel lookups.
202    ///
203    /// # Arguments
204    ///
205    /// * `registered_fd` - A registered file descriptor
206    ///
207    /// # Example
208    ///
209    /// ```rust,no_run
210    /// # use safer_ring::{operation::Operation, Registry};
211    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
212    /// let mut registry = Registry::new();
213    /// let registered_fd = registry.register_fd(0)?;
214    /// let op = Operation::read().registered_fd(registered_fd);
215    /// # Ok(())
216    /// # }
217    /// ```
218    #[inline]
219    pub fn registered_fd(mut self, registered_fd: RegisteredFd) -> Self {
220        self.fd = FdType::Registered(registered_fd);
221        self
222    }
223
224    /// Set a fixed file for this operation.
225    ///
226    /// Fixed files provide the best performance for frequently used files
227    /// by avoiding both file descriptor lookups and translation overhead.
228    /// The file must be pre-registered with the registry.
229    ///
230    /// # Arguments
231    ///
232    /// * `fixed_file` - A fixed file from the registry
233    ///
234    /// # Example
235    ///
236    /// ```rust,no_run
237    /// # use safer_ring::{operation::Operation, Registry};
238    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
239    /// let mut registry = Registry::new();
240    /// let fixed_files = registry.register_fixed_files(vec![0, 1])?;
241    /// let op = Operation::read().fixed_file(fixed_files[0].clone());
242    /// # Ok(())
243    /// # }
244    /// ```
245    #[inline]
246    pub fn fixed_file(mut self, fixed_file: crate::registry::FixedFile) -> Self {
247        self.fd = FdType::Fixed(fixed_file);
248        self
249    }
250
251    /// Set the buffer for this operation.
252    ///
253    /// The buffer lifetime must be at least as long as the ring lifetime
254    /// to ensure memory safety during the operation. The buffer must remain
255    /// pinned in memory until the operation completes.
256    ///
257    /// # Arguments
258    ///
259    /// * `buffer` - A pinned mutable slice that will be used for I/O
260    ///
261    /// # Example
262    ///
263    /// ```rust,no_run
264    /// # use safer_ring::operation::Operation;
265    /// # use std::pin::Pin;
266    /// let mut data = vec![0u8; 4096];
267    /// let op = Operation::read()
268    ///     .fd(0)
269    ///     .buffer(Pin::new(data.as_mut_slice()));
270    /// ```
271    #[inline]
272    pub fn buffer(mut self, buffer: Pin<&'buf mut [u8]>) -> Self {
273        self.buffer = BufferType::Pinned(buffer);
274        self
275    }
276
277    /// Set a registered buffer for this operation.
278    ///
279    /// Using registered buffers can improve performance by avoiding kernel
280    /// buffer validation and setup overhead.
281    ///
282    /// # Arguments
283    ///
284    /// * `registered_buffer` - A registered buffer
285    ///
286    /// # Example
287    ///
288    /// ```rust,no_run
289    /// # use safer_ring::{operation::Operation, Registry};
290    /// # use std::pin::Pin;
291    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
292    /// let mut registry = Registry::new();
293    /// let buffer = Pin::new(Box::new([0u8; 1024]));
294    /// let registered_buffer = registry.register_buffer(buffer)?;
295    /// let op = Operation::read()
296    ///     .fd(0)
297    ///     .registered_buffer(registered_buffer);
298    /// # Ok(())
299    /// # }
300    /// ```
301    #[inline]
302    pub fn registered_buffer(mut self, registered_buffer: RegisteredBuffer) -> Self {
303        self.buffer = BufferType::Registered(registered_buffer);
304        self
305    }
306
307    /// Set multiple buffers for vectored I/O operations.
308    ///
309    /// This enables scatter-gather I/O where data can be read into or written
310    /// from multiple non-contiguous buffers in a single operation.
311    ///
312    /// # Arguments
313    ///
314    /// * `buffers` - A vector of pinned mutable slices
315    ///
316    /// # Example
317    ///
318    /// ```rust,no_run
319    /// # use safer_ring::operation::Operation;
320    /// # use std::pin::Pin;
321    /// let mut buffer1 = vec![0u8; 512];
322    /// let mut buffer2 = vec![0u8; 512];
323    /// let buffers = vec![
324    ///     Pin::new(buffer1.as_mut_slice()),
325    ///     Pin::new(buffer2.as_mut_slice()),
326    /// ];
327    /// let op = Operation::read_vectored()
328    ///     .fd(0)
329    ///     .buffers(buffers);
330    /// ```
331    #[inline]
332    pub fn buffers(mut self, buffers: Vec<Pin<&'buf mut [u8]>>) -> Self {
333        self.buffer = BufferType::Vectored(buffers);
334        self
335    }
336
337    /// Set the offset for this operation.
338    ///
339    /// This is used for file operations to specify the position in the file.
340    /// For socket operations, this parameter is typically ignored by the kernel.
341    ///
342    /// # Arguments
343    ///
344    /// * `offset` - Byte offset in the file (0-based)
345    ///
346    /// # Example
347    ///
348    /// ```rust,no_run
349    /// # use safer_ring::operation::Operation;
350    /// let op = Operation::read()
351    ///     .fd(3)
352    ///     .offset(1024); // Read starting at byte 1024
353    /// ```
354    #[inline]
355    pub fn offset(mut self, offset: u64) -> Self {
356        self.offset = offset;
357        self
358    }
359
360    /// Get the current offset.
361    ///
362    /// Returns the byte offset for file operations, or 0 if not set.
363    #[inline]
364    pub fn get_offset(&self) -> u64 {
365        self.offset
366    }
367
368    /// Validate that the operation is ready for submission.
369    ///
370    /// This checks that all required fields are set for the operation type.
371    /// Using the type system's knowledge about operation requirements for
372    /// more efficient validation.
373    ///
374    /// # Errors
375    ///
376    /// Returns an error if:
377    /// - File descriptor is not set (< 0)
378    /// - Buffer is required but not set
379    /// - Vectored operation has empty buffer list
380    ///
381    /// # Example
382    ///
383    /// ```rust,no_run
384    /// # use safer_ring::operation::Operation;
385    /// let op = Operation::read().fd(0);
386    /// assert!(op.validate().is_err()); // Missing buffer
387    /// ```
388    pub fn validate(&self) -> Result<(), &'static str> {
389        // Check file descriptor
390        match &self.fd {
391            FdType::Raw(fd) if *fd < 0 => return Err("File descriptor must be set"),
392            _ => {}
393        }
394
395        // Use the type system's knowledge about buffer requirements
396        if self.op_type.requires_buffer() {
397            match &self.buffer {
398                BufferType::None => return Err("Buffer must be set for I/O operations"),
399                BufferType::Vectored(buffers) if buffers.is_empty() => {
400                    return Err("Vectored operations require at least one buffer")
401                }
402                _ => {}
403            }
404        }
405
406        // Validate operation type matches buffer type
407        if self.op_type.is_vectored() && !matches!(self.buffer, BufferType::Vectored(_)) {
408            return Err("Vectored operation types require vectored buffers");
409        }
410
411        if !self.op_type.is_vectored() && matches!(self.buffer, BufferType::Vectored(_)) {
412            return Err("Non-vectored operation types cannot use vectored buffers");
413        }
414
415        Ok(())
416    }
417
418    /// Convert this building operation to a submitted operation.
419    ///
420    /// This is typically called by the Ring when submitting the operation.
421    /// It validates the operation and transitions to the Submitted state.
422    ///
423    /// # Arguments
424    ///
425    /// * `id` - Unique operation ID assigned by the ring
426    ///
427    /// # Errors
428    ///
429    /// Returns an error if the operation is not properly configured.
430    #[allow(dead_code)]
431    pub(crate) fn submit_with_id(
432        self,
433        id: u64,
434    ) -> Result<Operation<'ring, 'buf, Submitted>, &'static str> {
435        self.validate()?;
436
437        // Zero-cost state transition - just change the type parameter
438        Ok(Operation {
439            ring: self.ring,
440            buffer: self.buffer,
441            fd: self.fd,
442            offset: self.offset,
443            op_type: self.op_type,
444            state: Submitted { id },
445        })
446    }
447}