mesh_protobuf/
transparent.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Transparent encoding for types that are a wrapper around another type.
5
6use crate::FieldDecode;
7use crate::FieldEncode;
8use crate::MessageDecode;
9use crate::MessageEncode;
10use crate::Result;
11use crate::inplace::InplaceOption;
12use crate::protobuf::FieldReader;
13use crate::protobuf::FieldSizer;
14use crate::protobuf::FieldWriter;
15use crate::protobuf::MessageReader;
16use crate::protobuf::MessageSizer;
17use crate::protobuf::MessageWriter;
18use crate::protofile::DescribeField;
19use crate::protofile::DescribeMessage;
20use crate::protofile::FieldType;
21use crate::protofile::MessageDescription;
22use crate::table::decode::DecoderEntry;
23use crate::table::encode::EncoderEntry;
24use core::mem::MaybeUninit;
25use core::ptr;
26
27/// A type that can be encoded as a transparent wrapper around an inner type.
28///
29/// # Safety
30/// The caller must ensure that there is only one non-zero-sized field in the
31/// struct, at the provided offset, and that constructing the struct by writing
32/// just that field is safe.
33pub unsafe trait Transparent {
34    /// The inner type.
35    type Inner;
36    /// The offset of the inner type. This should almost always be zero unless
37    /// Rust decides to add some padding at the beginning in some debug mode.
38    const OFFSET: usize;
39}
40
41/// An encoding derived by `mesh_derive` for a transparent type, using inner
42/// encoding `E`.
43#[derive(Copy, Clone)]
44pub struct TransparentEncoding<E>(E);
45
46impl<T: Transparent, E: DescribeField<T::Inner>> DescribeField<T> for TransparentEncoding<E> {
47    const FIELD_TYPE: FieldType<'static> = E::FIELD_TYPE;
48    const PACKED_TYPE: Option<&'static str> = E::PACKED_TYPE;
49}
50
51impl<T: Transparent, E: DescribeMessage<T::Inner>> DescribeMessage<T> for TransparentEncoding<E> {
52    const DESCRIPTION: MessageDescription<'static> = E::DESCRIPTION;
53}
54
55impl<T: Transparent, E: MessageEncode<T::Inner, R>, R> MessageEncode<T, R>
56    for TransparentEncoding<E>
57{
58    fn write_message(item: T, writer: MessageWriter<'_, '_, R>) {
59        let item = MaybeUninit::new(item);
60        // SAFETY: by the `Transparent` trait, there is a valid field with the
61        // inner type at the specified offset.
62        let inner = unsafe { item.as_ptr().byte_add(T::OFFSET).cast::<T::Inner>().read() };
63        E::write_message(inner, writer)
64    }
65
66    fn compute_message_size(item: &mut T, sizer: MessageSizer<'_>) {
67        // SAFETY: by the `Transparent` trait, there is a valid field with the
68        // inner type at the specified offset.
69        let inner = unsafe { &mut *ptr::from_mut(item).byte_add(T::OFFSET).cast::<T::Inner>() };
70        E::compute_message_size(inner, sizer)
71    }
72}
73
74impl<'de, T: Transparent, E: MessageDecode<'de, T::Inner, R>, R> MessageDecode<'de, T, R>
75    for TransparentEncoding<E>
76{
77    fn read_message(
78        item: &mut InplaceOption<'_, T>,
79        reader: MessageReader<'de, '_, R>,
80    ) -> Result<()> {
81        let init = item.forget();
82        // SAFETY: by the `Transparent` trait, the inner type is valid memory
83        // for write at the specified offset.
84        let inner = unsafe {
85            &mut *item
86                .as_mut_ptr()
87                .byte_add(T::OFFSET)
88                .cast::<MaybeUninit<T::Inner>>()
89        };
90        let mut inner = if init {
91            // SAFETY: the outer value is initialized, so the inner one is, too.
92            unsafe { InplaceOption::new_init_unchecked(inner) }
93        } else {
94            InplaceOption::uninit(inner)
95        };
96        let r = E::read_message(&mut inner, reader);
97        if inner.forget() {
98            // SAFETY: the inner value is initialized, so by the `Transparent`
99            // trait, the outer one is, too.
100            unsafe { item.set_init_unchecked() };
101        }
102        r
103    }
104}
105
106impl<T: Transparent, E: FieldEncode<T::Inner, R>, R> FieldEncode<T, R> for TransparentEncoding<E> {
107    fn write_field(item: T, writer: FieldWriter<'_, '_, R>) {
108        let item = MaybeUninit::new(item);
109        // SAFETY: by the `Transparent` trait, there is a valid field with the
110        // inner type at the specified offset.
111        let inner = unsafe { item.as_ptr().byte_add(T::OFFSET).cast::<T::Inner>().read() };
112        E::write_field(inner, writer)
113    }
114
115    fn compute_field_size(item: &mut T, sizer: FieldSizer<'_>) {
116        // SAFETY: by the `Transparent` trait, there is a valid field with the
117        // inner type at the specified offset.
118        let inner = unsafe { &mut *ptr::from_mut(item).byte_add(T::OFFSET).cast::<T::Inner>() };
119        E::compute_field_size(inner, sizer)
120    }
121
122    fn wrap_in_sequence() -> bool {
123        E::wrap_in_sequence()
124    }
125
126    const ENTRY: EncoderEntry<T, R> = {
127        // If there is no leading padding, then just use the inner entry
128        // directly.
129        if T::OFFSET == 0 {
130            // SAFETY: by the `Transparent` trait, there is a valid field with
131            // the inner type, and we know it to be offset zero. So we can
132            // encode this type by encoding it as if it is the inner type.
133            unsafe { EncoderEntry::new_unchecked(E::ENTRY.erase()) }
134        } else {
135            // We could probably use a table entry here, but in practice this
136            // path won't hit so don't bother.
137            EncoderEntry::custom::<Self>()
138        }
139    };
140}
141
142impl<'de, T: Transparent, E: FieldDecode<'de, T::Inner, R>, R> FieldDecode<'de, T, R>
143    for TransparentEncoding<E>
144{
145    fn read_field(item: &mut InplaceOption<'_, T>, reader: FieldReader<'de, '_, R>) -> Result<()> {
146        let init = item.forget();
147        // SAFETY: by the `Transparent` trait, the inner type is valid memory
148        // for write at the specified offset.
149        let inner = unsafe {
150            &mut *item
151                .as_mut_ptr()
152                .byte_add(T::OFFSET)
153                .cast::<MaybeUninit<T::Inner>>()
154        };
155        let mut inner = if init {
156            // SAFETY: the outer value is initialized, so the inner one is, too.
157            unsafe { InplaceOption::new_init_unchecked(inner) }
158        } else {
159            InplaceOption::uninit(inner)
160        };
161        let r = E::read_field(&mut inner, reader);
162        if inner.forget() {
163            // SAFETY: the inner value is initialized, so by the `Transparent`
164            // trait, the outer one is, too.
165            unsafe { item.set_init_unchecked() };
166        }
167        r
168    }
169
170    fn default_field(item: &mut InplaceOption<'_, T>) -> Result<()> {
171        let init = item.forget();
172        // SAFETY: by the `Transparent` trait, the inner type is valid memory
173        // for write at the specified offset.
174        let inner = unsafe {
175            &mut *item
176                .as_mut_ptr()
177                .byte_add(T::OFFSET)
178                .cast::<MaybeUninit<T::Inner>>()
179        };
180        let mut inner = if init {
181            // SAFETY: the outer value is initialized, so the inner one is, too.
182            unsafe { InplaceOption::new_init_unchecked(inner) }
183        } else {
184            InplaceOption::uninit(inner)
185        };
186        let r = E::default_field(&mut inner);
187        if inner.forget() {
188            // SAFETY: the inner value is initialized, so by the `Transparent`
189            // trait, the outer one is, too.
190            unsafe { item.set_init_unchecked() };
191        }
192        r
193    }
194
195    fn wrap_in_sequence() -> bool {
196        E::wrap_in_sequence()
197    }
198
199    const ENTRY: DecoderEntry<'de, T, R> = {
200        // If there is no leading padding, then just use the inner entry
201        // directly.
202        if T::OFFSET == 0 {
203            // SAFETY: by the `Transparent` trait, the outer type can be
204            // initialized by initializing the inner type, and we know it to be
205            // at offset zero. So we can decode this type by decoding it as if
206            // it is the inner type.
207            unsafe { DecoderEntry::new_unchecked(E::ENTRY.erase()) }
208        } else {
209            // We could probably use a table entry here, but in practice this
210            // path won't hit so don't bother.
211            DecoderEntry::custom::<Self>()
212        }
213    };
214}