Skip to main content

dope_core/io/socket/msg/
raw.rs

1use core::{mem, num::NonZero};
2
3use dope_sys::io::socket::{self, msg, msg::Header};
4use o3::{
5    buffer,
6    depot::array::{self, copy},
7};
8
9use crate::io::transfer;
10
11pub trait Iovec {
12    /// Constructs a retained iovec.
13    /// # Safety
14    /// The bytes stay fixed through terminal completion or quiescence.
15    unsafe fn retain(self) -> msg::Iovec;
16}
17
18impl Iovec for &[u8] {
19    unsafe fn retain(self) -> msg::Iovec {
20        msg::Iovec::from_slice(self)
21    }
22}
23
24pub trait Vectored<'a, const N: usize = { super::MAX_IOVECS }> {
25    /// Constructs a retained descriptor graph.
26    /// # Safety
27    /// Inputs stay fixed, count fits `N` and `MAX_IOVECS`, and `bytes` is the exact sum.
28    unsafe fn retain(self) -> super::Vectored<'a, N>;
29}
30
31impl<'a, const N: usize> Vectored<'a, N>
32    for (&'a mut Header, &'a copy::Vec<msg::Iovec, N>, transfer::Len)
33{
34    unsafe fn retain(self) -> super::Vectored<'a, N> {
35        const { assert!(N <= super::MAX_IOVECS) };
36        let (header, iovecs, bytes) = self;
37        super::Vectored::from_parts(header, iovecs.as_slice(), bytes)
38    }
39}
40
41impl<'a, const N: usize> Vectored<'a, N>
42    for (
43        &'a mut Header,
44        &'a [msg::Iovec; N],
45        array::Len<N>,
46        transfer::Len,
47    )
48{
49    unsafe fn retain(self) -> super::Vectored<'a, N> {
50        const { assert!(N <= super::MAX_IOVECS) };
51        let (header, iovecs, count, bytes) = self;
52        super::Vectored::from_parts(header, count.prefix(iovecs), bytes)
53    }
54}
55
56pub(super) trait Project<'a> {
57    fn slice(self) -> &'a [u8];
58}
59
60impl<'a> Project<'a> for &'a msg::Iovec {
61    fn slice(self) -> &'a [u8] {
62        // SAFETY: construction proves the bytes live for the graph lifetime.
63        unsafe { socket::raw::Iovec::as_slice(self) }
64    }
65}
66
67impl<'a, const N: usize> super::Vectored<'a, N> {
68    /// Projects a sole descriptor with its admitted byte bound.
69    pub fn contiguous(&self) -> Option<transfer::View<'a>> {
70        let [iovec] = self.iovs else {
71            return None;
72        };
73        // SAFETY: the sole descriptor's length equals the admitted message length.
74        let bytes = unsafe { buffer::bytes::bounded::raw::Input::new(iovec.slice()) };
75        Some(transfer::View::from_prefix(
76            buffer::bytes::Prefix::from_raw(bytes),
77        ))
78    }
79}
80
81/// Compact exact cursor over one retained iovec graph.
82#[derive(Clone, Copy)]
83pub struct Cursor<'a> {
84    iovecs: &'a [msg::Iovec],
85    index: u32,
86    local: u32,
87    remaining: transfer::Len,
88}
89
90impl<'a> Cursor<'a> {
91    pub const fn bytes(&self) -> transfer::Len {
92        self.remaining
93    }
94
95    fn start<const N: usize>(source: &super::Vectored<'a, N>) -> Self {
96        Self {
97            iovecs: source.iovs,
98            index: 0,
99            local: 0,
100            remaining: source.bytes(),
101        }
102    }
103
104    fn new<const N: usize>(source: &super::Vectored<'a, N>, mut offset: usize) -> Option<Self> {
105        let len = source.bytes().into_raw();
106        if offset > len {
107            return None;
108        }
109        let left = transfer::Len::prefix(len - offset, source.bytes());
110        let iovecs: &'a [msg::Iovec] = source.iovs;
111        let mut index = 0;
112        let mut local = 0_usize;
113        while let Some(iovec) = iovecs.get(index as usize) {
114            if offset < iovec.len() {
115                local = offset;
116                break;
117            }
118            offset -= iovec.len();
119            index += 1;
120        }
121        Some(Self {
122            iovecs,
123            index,
124            local: local as u32,
125            remaining: left,
126        })
127    }
128}
129
130// SAFETY: retained construction proves the iovec graph's exact byte sum and
131// stable backing for 'a; cursor transitions emit each capped prefix once.
132unsafe impl<'a: 'b, 'b> buffer::raw::Scatter<'b> for Cursor<'a> {
133    fn remaining(&self) -> usize {
134        self.remaining.into_raw()
135    }
136
137    unsafe fn advance<const MAX: usize>(
138        &mut self,
139        cap: NonZero<usize>,
140    ) -> buffer::raw::Chunk<'b, MAX> {
141        loop {
142            // SAFETY: admitted nonzero cap proves remaining bytes in this retained graph.
143            let iovec = unsafe { self.iovecs.get_unchecked(self.index as usize) };
144            if iovec.len() == self.local as usize {
145                self.index += 1;
146                self.local = 0;
147                continue;
148            }
149            // SAFETY: the non-empty retained iovec has live backing bytes;
150            // cursor construction and transitions keep `local` within it.
151            let bytes = unsafe { iovec.slice().get_unchecked(self.local as usize..) };
152            let len = bytes.len().min(cap.get());
153            self.local += len as u32;
154            let consumed = transfer::Len::prefix(len, self.remaining);
155            self.remaining = self.remaining.abs_diff(consumed);
156            if self.local as usize == iovec.len() {
157                self.index += 1;
158                self.local = 0;
159            }
160            // SAFETY: retained graph construction proves exact length and
161            // stable bytes; the cursor emits one non-empty capped prefix.
162            return unsafe { buffer::raw::Chunk::new(&bytes[..len]) };
163        }
164    }
165}
166
167// SAFETY: Vectored retains its complete descriptor graph for 'a, and each cursor
168// derives its position and remaining length from that same proved graph.
169unsafe impl<'a, const N: usize> buffer::raw::Chunks for super::Vectored<'a, N> {
170    type Cursor<'data>
171        = Cursor<'a>
172    where
173        Self: 'data;
174
175    fn len(&self) -> usize {
176        self.bytes().into_raw()
177    }
178
179    fn start(&self) -> Self::Cursor<'_> {
180        Cursor::start(self)
181    }
182
183    fn cursor(&self, offset: usize) -> Option<Self::Cursor<'_>> {
184        Cursor::new(self, offset)
185    }
186}
187
188const _: () = {
189    let state = mem::size_of::<&[msg::Iovec]>() + 3 * mem::size_of::<u32>();
190    let align = mem::align_of::<Cursor<'_>>();
191    let exact = state.div_ceil(align) * align;
192    assert!(mem::size_of::<Cursor<'_>>() == exact);
193};