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