|
| 1 | +use std::{any::type_name, mem::transmute}; |
| 2 | + |
| 3 | +pub use bincode; |
| 4 | +use bincode::{ |
| 5 | + de::Decoder, |
| 6 | + enc::Encoder, |
| 7 | + error::{DecodeError, EncodeError}, |
| 8 | +}; |
| 9 | + |
| 10 | +use crate::{TurboBincodeDecode, TurboBincodeDecoder, TurboBincodeEncode, TurboBincodeEncoder}; |
| 11 | + |
| 12 | +#[track_caller] |
| 13 | +pub fn encode_for_turbo_bincode_encode_impl<'a, T: TurboBincodeEncode, E: Encoder>( |
| 14 | + value: &T, |
| 15 | + encoder: &'a mut E, |
| 16 | +) -> Result<(), EncodeError> { |
| 17 | + let encoder = if unty::type_equal::<E, TurboBincodeEncoder>() { |
| 18 | + // SAFETY: Transmute is safe because `&mut E` is `&mut TurboBincodeEncoder`: |
| 19 | + // - `unty::type_equal::<E, TurboBincodeEncoder>()` does not check lifetimes, but does check |
| 20 | + // the type and layout, so we know those are correct. |
| 21 | + // - The transmuted encoder cannot escape this function, and we know that the lifetime of |
| 22 | + // `'a` is at least as long as the function. |
| 23 | + // - Lifetimes don't change layout. This is not strictly guaranteed, but if this assumption |
| 24 | + // is broken, we'd have a different type id (type ids are derived from layout |
| 25 | + // information), `type_equal` would return `false`, and we'd panic instead of violating |
| 26 | + // memory safety. |
| 27 | + // - Two mutable references have the same layout and alignment when they reference exactly |
| 28 | + // the same type. |
| 29 | + // - The explicit lifetime ('a) avoids creating an implitly unbounded lifetime. |
| 30 | + unsafe { transmute::<&'a mut E, &'a mut TurboBincodeEncoder>(encoder) } |
| 31 | + } else { |
| 32 | + unreachable!( |
| 33 | + "{} implements TurboBincodeEncode, but was called with a {} encoder implementation", |
| 34 | + type_name::<T>(), |
| 35 | + type_name::<E>(), |
| 36 | + ) |
| 37 | + }; |
| 38 | + TurboBincodeEncode::encode(value, encoder) |
| 39 | +} |
| 40 | + |
| 41 | +#[track_caller] |
| 42 | +pub fn decode_for_turbo_bincode_decode_impl< |
| 43 | + 'a, |
| 44 | + Context, |
| 45 | + T: TurboBincodeDecode<Context>, |
| 46 | + D: Decoder<Context = Context>, |
| 47 | +>( |
| 48 | + decoder: &'a mut D, |
| 49 | +) -> Result<T, DecodeError> { |
| 50 | + let decoder = if unty::type_equal::<D, TurboBincodeDecoder>() { |
| 51 | + // SAFETY: See notes on the `Encode::encode` implementation on |
| 52 | + // `encode_for_turbo_bincode_encode_impl`. |
| 53 | + unsafe { transmute::<&'a mut D, &'a mut TurboBincodeDecoder<'a>>(decoder) } |
| 54 | + } else { |
| 55 | + unreachable!( |
| 56 | + "{} implements TurboBincodeDecode, but was called with a {} decoder implementation", |
| 57 | + type_name::<T>(), |
| 58 | + type_name::<D>(), |
| 59 | + ) |
| 60 | + }; |
| 61 | + TurboBincodeDecode::decode(decoder) |
| 62 | +} |
0 commit comments