Skip to main content

dope_session/
codec.rs

1use crate::pool::slot::WRITE_BUF_CAP;
2
3/// Storage abstraction for a slot's outbound staging buffer.
4///
5/// Stable Rust can't write `[u8; <L as CodecLayer>::WRITE_BUF_CAP]` because
6/// the array length isn't known unless `generic_const_exprs` is enabled, so
7/// the buffer type is associated rather than const-sized: `()` for layers
8/// that don't stage (zero bytes per slot), [`WriteBufArr`] for layers that
9/// do.
10pub trait WriteBufStorage: Default + 'static {
11    fn as_slice(&self) -> &[u8];
12    fn as_mut_slice(&mut self) -> &mut [u8];
13}
14
15impl WriteBufStorage for () {
16    #[inline(always)]
17    fn as_slice(&self) -> &[u8] {
18        &[]
19    }
20    #[inline(always)]
21    fn as_mut_slice(&mut self) -> &mut [u8] {
22        &mut []
23    }
24}
25
26/// Inline staging buffer used by codec layers. Newtype because `[u8; N]:
27/// Default` only holds for small `N` on stable Rust.
28#[repr(transparent)]
29pub struct WriteBufArr(pub(crate) [u8; WRITE_BUF_CAP]);
30
31impl Default for WriteBufArr {
32    #[inline(always)]
33    fn default() -> Self {
34        Self([0u8; WRITE_BUF_CAP])
35    }
36}
37
38impl WriteBufStorage for WriteBufArr {
39    #[inline(always)]
40    fn as_slice(&self) -> &[u8] {
41        &self.0
42    }
43    #[inline(always)]
44    fn as_mut_slice(&mut self) -> &mut [u8] {
45        &mut self.0
46    }
47}
48
49/// Pool-side dispatch for a connection's wire-flow.
50///
51/// Two kinds of impls exist:
52///
53/// * Passthrough — [`Plain`]. `IS_PASSTHROUGH = true`, `WriteBuf = ()` (ZST,
54///   no per-slot staging cost), all codec methods are no-ops because pool
55///   skips them via the const branch.
56/// * Codec — e.g. `dope_tls::TlsCodec`. `IS_PASSTHROUGH = false`,
57///   `WriteBuf = WriteBufArr`, all codec methods do real work.
58///
59/// Pool generics are over `L: CodecLayer`. Real codecs implement this trait
60/// directly — there is no separate `Codec` / `Coded<C>` indirection.
61///
62/// Protocols that require a non-passthrough layer (e.g. a PostgreSQL client
63/// that performs STARTTLS) bound on the concrete layer type:
64///
65/// ```ignore
66/// type CodecLayer = dope_tls::LazyTlsCodec;
67/// ```
68pub trait CodecLayer: 'static {
69    type ConnState: Default + 'static;
70    type WriteBuf: WriteBufStorage;
71
72    /// True if this layer bypasses the codec staging path.
73    /// Pool branches on this const to pick the direct fill_msghdr path
74    /// (passthrough) vs the write_buf → process_outbound → take_inflight
75    /// path (codec). The branch is monomorphized → DCE.
76    const IS_PASSTHROUGH: bool;
77
78    fn is_active(state: &Self::ConnState) -> bool;
79    fn process_inbound(state: &mut Self::ConnState, wire_in: &[u8]);
80    fn process_outbound(state: &mut Self::ConnState, plaintext_in: &[u8]);
81    fn take_plaintext(state: &mut Self::ConnState) -> Vec<u8>;
82    fn install_plaintext(state: &mut Self::ConnState, plaintext: Vec<u8>);
83    fn take_inflight(state: &mut Self::ConnState) -> Option<(*const u8, u32)>;
84    fn consume_inflight(state: &mut Self::ConnState, n: usize) -> bool;
85    fn has_inflight(state: &Self::ConnState) -> bool;
86    fn wire_slice(state: &Self::ConnState) -> &[u8];
87    fn close_pending(state: &Self::ConnState) -> bool;
88}
89
90/// Passthrough layer: no codec, no staging buffer, no codec state.
91///
92/// All trait methods are no-ops; pool's `IS_PASSTHROUGH` branch never
93/// dispatches them.
94pub struct Plain;
95
96impl CodecLayer for Plain {
97    type ConnState = ();
98    type WriteBuf = ();
99    const IS_PASSTHROUGH: bool = true;
100
101    #[inline(always)]
102    fn is_active(_: &()) -> bool {
103        true
104    }
105    #[inline(always)]
106    fn process_inbound(_: &mut (), _: &[u8]) {}
107    #[inline(always)]
108    fn process_outbound(_: &mut (), _: &[u8]) {}
109    #[inline(always)]
110    fn take_plaintext(_: &mut ()) -> Vec<u8> {
111        Vec::new()
112    }
113    #[inline(always)]
114    fn install_plaintext(_: &mut (), _: Vec<u8>) {}
115    #[inline(always)]
116    fn take_inflight(_: &mut ()) -> Option<(*const u8, u32)> {
117        None
118    }
119    #[inline(always)]
120    fn consume_inflight(_: &mut (), _: usize) -> bool {
121        false
122    }
123    #[inline(always)]
124    fn has_inflight(_: &()) -> bool {
125        false
126    }
127    #[inline(always)]
128    fn wire_slice(_: &()) -> &[u8] {
129        &[]
130    }
131    #[inline(always)]
132    fn close_pending(_: &()) -> bool {
133        false
134    }
135}