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
67/// Compact exact cursor over one retained iovec graph.
68#[derive(Clone, Copy)]
69pub struct Cursor<'a> {
70    iovecs: &'a [msg::Iovec],
71    index: u32,
72    local: u32,
73    remaining: transfer::Len,
74}
75
76impl<'a> Cursor<'a> {
77    pub const fn bytes(&self) -> transfer::Len {
78        self.remaining
79    }
80
81    fn start<const N: usize>(source: &super::Vectored<'a, N>) -> Self {
82        Self {
83            iovecs: source.iovs,
84            index: 0,
85            local: 0,
86            remaining: source.bytes(),
87        }
88    }
89
90    fn new<const N: usize>(source: &super::Vectored<'a, N>, mut offset: usize) -> Option<Self> {
91        let len = source.bytes().into_raw();
92        if offset > len {
93            return None;
94        }
95        let left = transfer::Len::prefix(len - offset, source.bytes());
96        let iovecs: &'a [msg::Iovec] = source.iovs;
97        let mut index = 0;
98        let mut local = 0_usize;
99        while let Some(iovec) = iovecs.get(index as usize) {
100            if offset < iovec.len() {
101                local = offset;
102                break;
103            }
104            offset -= iovec.len();
105            index += 1;
106        }
107        Some(Self {
108            iovecs,
109            index,
110            local: local as u32,
111            remaining: left,
112        })
113    }
114}
115
116// SAFETY: retained construction proves the iovec graph's exact byte sum and
117// stable backing for 'a; cursor transitions emit each capped prefix once.
118unsafe impl<'a: 'b, 'b> buffer::raw::Scatter<'b> for Cursor<'a> {
119    fn remaining(&self) -> usize {
120        self.remaining.into_raw()
121    }
122
123    unsafe fn advance<const MAX: usize>(
124        &mut self,
125        cap: NonZero<usize>,
126        _left: NonZero<usize>,
127    ) -> buffer::raw::Chunk<'b, MAX> {
128        loop {
129            // SAFETY: `left` is the exact positive remaining length and the
130            // retained graph's byte sum proves a backing iovec exists.
131            let iovec = unsafe { self.iovecs.get_unchecked(self.index as usize) };
132            if iovec.len() == self.local as usize {
133                self.index += 1;
134                self.local = 0;
135                continue;
136            }
137            // SAFETY: the non-empty retained iovec has live backing bytes;
138            // cursor construction and transitions keep `local` within it.
139            let bytes = unsafe { iovec.slice().get_unchecked(self.local as usize..) };
140            let len = bytes.len().min(cap.get());
141            self.local += len as u32;
142            let consumed = transfer::Len::prefix(len, self.remaining);
143            self.remaining = self.remaining.abs_diff(consumed);
144            if self.local as usize == iovec.len() {
145                self.index += 1;
146                self.local = 0;
147            }
148            // SAFETY: retained graph construction proves exact length and
149            // stable bytes; the cursor emits one non-empty capped prefix.
150            return unsafe { buffer::raw::Chunk::new(&bytes[..len]) };
151        }
152    }
153}
154
155// SAFETY: Vectored retains its complete descriptor graph for 'a, and each cursor
156// derives its position and remaining length from that same proved graph.
157unsafe impl<'a, const N: usize> buffer::raw::Chunks for super::Vectored<'a, N> {
158    type Cursor<'data>
159        = Cursor<'a>
160    where
161        Self: 'data;
162
163    fn len(&self) -> usize {
164        self.bytes().into_raw()
165    }
166
167    fn start(&self) -> Self::Cursor<'_> {
168        Cursor::start(self)
169    }
170
171    fn cursor(&self, offset: usize) -> Option<Self::Cursor<'_>> {
172        Cursor::new(self, offset)
173    }
174}
175
176const _: () = {
177    let state = mem::size_of::<&[msg::Iovec]>() + 3 * mem::size_of::<u32>();
178    let align = mem::align_of::<Cursor<'_>>();
179    let exact = state.div_ceil(align) * align;
180    assert!(mem::size_of::<Cursor<'_>>() == exact);
181};