mproto/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(all(not(feature = "std"), feature = "alloc"))]
4extern crate alloc;
5
6use core::{ops::Deref, pin::Pin};
7
8pub use boxed::BoxLazy;
9pub use decode_cursor::DecodeCursor;
10pub use encode_cursor::EncodeCursor;
11pub use list::{ListGen, ListLazy};
12
13mod boxed;
14mod copy_primitives;
15mod decode_cursor;
16mod encode_cursor;
17mod list;
18mod option;
19mod result;
20mod string;
21#[cfg(test)]
22mod tests;
23
24pub trait BaseLen {
25    const BASE_LEN: usize;
26}
27
28pub trait Encode: BaseLen {
29    fn scratch_len(&self) -> usize;
30
31    fn encode(&self, cursor: &mut EncodeCursor);
32}
33
34#[derive(Debug)]
35pub struct DecodeError;
36
37impl core::fmt::Display for DecodeError {
38    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39        write!(f, "failed to decode mproto value")
40    }
41}
42
43impl core::error::Error for DecodeError {}
44
45pub type DecodeResult<T> = Result<T, DecodeError>;
46
47pub trait Decode<'a>: BaseLen + Sized {
48    fn decode(cursor: &DecodeCursor<'a>) -> DecodeResult<Self>;
49}
50
51pub trait Compatible<Other: ?Sized>: Encode {}
52
53pub trait Owned: Encode + for<'a> Decode<'a> + Compatible<Self> + Clone + Send + Sync + 'static {
54    type Lazy<'a>: Lazy<'a, Owned = Self>;
55
56    fn lazy_to_owned(lazy: Self::Lazy<'_>) -> DecodeResult<Self>;
57}
58
59pub trait Lazy<'a>: Encode + Decode<'a> + Copy + Clone + PartialEq + core::fmt::Debug {
60    type Owned: Owned<Lazy<'a> = Self>;
61}
62
63#[inline]
64pub fn encoded_len<T: Encode>(value: T) -> usize {
65    T::BASE_LEN + value.scratch_len()
66}
67
68#[inline]
69pub fn encode_value<E: Encode>(v: E, mut buf: impl AsMut<[u8]>) -> usize {
70    let mut cursor = EncodeCursor::new::<E>(buf.as_mut());
71    v.encode(&mut cursor);
72    cursor.encoded_len()
73}
74
75#[cfg(any(feature = "std", feature = "alloc"))]
76#[inline]
77pub fn encode_value_vec<E: Encode>(v: E) -> Vec<u8> {
78    let mut buf = vec![0u8; encoded_len(&v)];
79    let mut cursor = EncodeCursor::new::<E>(buf.as_mut());
80    v.encode(&mut cursor);
81    buf
82}
83
84#[inline]
85pub fn decode_value<'a, D: Decode<'a>>(buf: &'a [u8]) -> DecodeResult<D> {
86    Decode::decode(&DecodeCursor::new(buf))
87}
88
89// TODO Consider using Yoke, either directly in apps or wrapping it here.
90pub struct LazyBuf<T: Owned, B> {
91    buf: Pin<B>,
92    lazy: T::Lazy<'static>,
93}
94
95mod sealed {
96    // workaround for compiler limitation
97    // https://github.com/rust-lang/rust/issues/49601#issuecomment-1007884546
98    pub trait LazyBufMapFn<T, U>: FnOnce(T) -> U {}
99    impl<F, T, U> LazyBufMapFn<T, U> for F where F: FnOnce(T) -> U {}
100}
101
102impl<T: Owned, B: Deref<Target = [u8]> + core::marker::Unpin> LazyBuf<T, B> {
103    #[inline]
104    pub fn new(buf: B) -> Self {
105        let buf = Pin::new(buf);
106        let lazy: T::Lazy<'_> = decode_value(buf.as_ref().get_ref().as_ref()).unwrap();
107        // TODO is this actually safe to do?
108        // Erase lifetime of lazy value
109        let lazy = unsafe { core::mem::transmute(lazy) };
110        Self { buf, lazy }
111    }
112
113    #[inline]
114    pub fn get<'a>(&'a self) -> T::Lazy<'a> {
115        // TODO is this actually safe to do?
116        unsafe { core::mem::transmute(self.lazy.clone()) }
117    }
118
119    #[inline]
120    pub fn map<U: Owned, F>(self, f: F) -> LazyBuf<U, B>
121    where
122        F: for<'a> sealed::LazyBufMapFn<T::Lazy<'a>, U::Lazy<'a>>,
123    {
124        let lazy = f(self.lazy);
125        // TODO is this actually safe to do?
126        // Erase lifetime of lazy value
127        let lazy = unsafe { core::mem::transmute(lazy) };
128        LazyBuf {
129            buf: self.buf,
130            lazy,
131        }
132    }
133}
134
135impl<T, U: Compatible<T> + ?Sized> Compatible<T> for &U {}
136
137impl<T: BaseLen + ?Sized> BaseLen for &T {
138    const BASE_LEN: usize = T::BASE_LEN;
139}
140
141impl<T: Encode + ?Sized> Encode for &T {
142    #[inline]
143    fn scratch_len(&self) -> usize {
144        T::scratch_len(self)
145    }
146
147    #[inline]
148    fn encode(&self, cursor: &mut EncodeCursor) {
149        T::encode(self, cursor);
150    }
151}
152
153impl<T: BaseLen + ?Sized> BaseLen for &mut T {
154    const BASE_LEN: usize = T::BASE_LEN;
155}
156
157impl<T: Encode + ?Sized> Encode for &mut T {
158    #[inline]
159    fn scratch_len(&self) -> usize {
160        T::scratch_len(self)
161    }
162
163    #[inline]
164    fn encode(&self, cursor: &mut EncodeCursor) {
165        T::encode(self, cursor);
166    }
167}
168
169pub const fn max(a: usize, b: usize) -> usize {
170    [a, b][(a < b) as usize]
171}