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