1 // This file is part of ICU4X. For terms of use, please see the file 2 // called LICENSE at the top level of the ICU4X source tree 3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). 4 5 use core::any::Any; 6 7 use crate::prelude::*; 8 use crate::ule::MaybeEncodeAsVarULE; 9 use crate::{dynutil::UpcastDataPayload, ule::MaybeAsVarULE}; 10 use alloc::sync::Arc; 11 use databake::{Bake, BakeSize, CrateEnv, TokenStream}; 12 use yoke::*; 13 use zerovec::VarZeroVec; 14 15 #[cfg(doc)] 16 use zerovec::ule::VarULE; 17 18 trait ExportableDataPayload { bake_yoke(&self, ctx: &CrateEnv) -> TokenStream19 fn bake_yoke(&self, ctx: &CrateEnv) -> TokenStream; bake_size(&self) -> usize20 fn bake_size(&self) -> usize; serialize_yoke( &self, serializer: &mut dyn erased_serde::Serializer, ) -> Result<(), DataError>21 fn serialize_yoke( 22 &self, 23 serializer: &mut dyn erased_serde::Serializer, 24 ) -> Result<(), DataError>; maybe_bake_varule_encoded( &self, rest: &[&DataPayload<ExportMarker>], ctx: &CrateEnv, ) -> Option<TokenStream>25 fn maybe_bake_varule_encoded( 26 &self, 27 rest: &[&DataPayload<ExportMarker>], 28 ctx: &CrateEnv, 29 ) -> Option<TokenStream>; as_any(&self) -> &dyn Any30 fn as_any(&self) -> &dyn Any; eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool31 fn eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool; 32 } 33 34 impl<M: DynamicDataMarker> ExportableDataPayload for DataPayload<M> 35 where 36 for<'a> <M::DataStruct as Yokeable<'a>>::Output: 37 Bake + BakeSize + serde::Serialize + MaybeEncodeAsVarULE + PartialEq, 38 { bake_yoke(&self, ctx: &CrateEnv) -> TokenStream39 fn bake_yoke(&self, ctx: &CrateEnv) -> TokenStream { 40 self.get().bake(ctx) 41 } 42 bake_size(&self) -> usize43 fn bake_size(&self) -> usize { 44 core::mem::size_of::<<M::DataStruct as Yokeable>::Output>() + self.get().borrows_size() 45 } 46 serialize_yoke( &self, serializer: &mut dyn erased_serde::Serializer, ) -> Result<(), DataError>47 fn serialize_yoke( 48 &self, 49 serializer: &mut dyn erased_serde::Serializer, 50 ) -> Result<(), DataError> { 51 use erased_serde::Serialize; 52 self.get() 53 .erased_serialize(serializer) 54 .map_err(|e| DataError::custom("Serde export").with_display_context(&e))?; 55 Ok(()) 56 } 57 maybe_bake_varule_encoded( &self, rest: &[&DataPayload<ExportMarker>], ctx: &CrateEnv, ) -> Option<TokenStream>58 fn maybe_bake_varule_encoded( 59 &self, 60 rest: &[&DataPayload<ExportMarker>], 61 ctx: &CrateEnv, 62 ) -> Option<TokenStream> { 63 let first_varule = self.get().maybe_encode_as_varule()?; 64 let recovered_vec: Vec< 65 &<<M::DataStruct as Yokeable<'_>>::Output as MaybeAsVarULE>::EncodedStruct, 66 > = core::iter::once(first_varule) 67 .chain(rest.iter().map(|v| { 68 #[allow(clippy::expect_used)] // exporter code 69 v.get() 70 .payload 71 .as_any() 72 .downcast_ref::<Self>() 73 .expect("payloads expected to be same type") 74 .get() 75 .maybe_encode_as_varule() 76 .expect("MaybeEncodeAsVarULE impl should be symmetric") 77 })) 78 .collect(); 79 let vzv: VarZeroVec< 80 <<M::DataStruct as Yokeable<'_>>::Output as MaybeAsVarULE>::EncodedStruct, 81 > = VarZeroVec::from(&recovered_vec); 82 let vzs = vzv.as_slice(); 83 Some(vzs.bake(ctx)) 84 } 85 as_any(&self) -> &dyn Any86 fn as_any(&self) -> &dyn Any { 87 self 88 } 89 eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool90 fn eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool { 91 match other.as_any().downcast_ref::<Self>() { 92 Some(downcasted) => (*self).eq(downcasted), 93 None => { 94 debug_assert!( 95 false, 96 "cannot compare ExportableDataPayloads of different types: self is {:?} but other is {:?}", 97 self.type_id(), 98 other.as_any().type_id(), 99 ); 100 false 101 } 102 } 103 } 104 } 105 106 #[doc(hidden)] // macro 107 #[derive(yoke::Yokeable, Clone)] 108 pub struct ExportBox { 109 payload: Arc<dyn ExportableDataPayload + Sync + Send>, 110 } 111 112 impl PartialEq for ExportBox { eq(&self, other: &Self) -> bool113 fn eq(&self, other: &Self) -> bool { 114 self.payload.eq_dyn(&*other.payload) 115 } 116 } 117 118 impl Eq for ExportBox {} 119 120 impl core::fmt::Debug for ExportBox { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result121 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 122 f.debug_struct("ExportBox") 123 .field("payload", &"<payload>") 124 .finish() 125 } 126 } 127 128 impl<M> UpcastDataPayload<M> for ExportMarker 129 where 130 M: DynamicDataMarker, 131 M::DataStruct: Sync + Send, 132 for<'a> <M::DataStruct as Yokeable<'a>>::Output: 133 Bake + BakeSize + serde::Serialize + MaybeEncodeAsVarULE + PartialEq, 134 { upcast(other: DataPayload<M>) -> DataPayload<ExportMarker>135 fn upcast(other: DataPayload<M>) -> DataPayload<ExportMarker> { 136 DataPayload::from_owned(ExportBox { 137 payload: Arc::new(other), 138 }) 139 } 140 } 141 142 impl DataPayload<ExportMarker> { 143 /// Serializes this [`DataPayload`] into a serializer using Serde. 144 /// 145 /// # Examples 146 /// 147 /// ``` 148 /// use icu_provider::dynutil::UpcastDataPayload; 149 /// use icu_provider::export::*; 150 /// use icu_provider::hello_world::HelloWorldV1; 151 /// use icu_provider::prelude::*; 152 /// 153 /// // Create an example DataPayload 154 /// let payload: DataPayload<HelloWorldV1> = Default::default(); 155 /// let export: DataPayload<ExportMarker> = UpcastDataPayload::upcast(payload); 156 /// 157 /// // Serialize the payload to a JSON string 158 /// let mut buffer: Vec<u8> = vec![]; 159 /// export 160 /// .serialize(&mut serde_json::Serializer::new(&mut buffer)) 161 /// .expect("Serialization should succeed"); 162 /// assert_eq!(r#"{"message":"(und) Hello World"}"#.as_bytes(), buffer); 163 /// ``` serialize<S>(&self, serializer: S) -> Result<(), DataError> where S: serde::Serializer, S::Ok: 'static,164 pub fn serialize<S>(&self, serializer: S) -> Result<(), DataError> 165 where 166 S: serde::Serializer, 167 S::Ok: 'static, // erased_serde requirement, cannot return values in `Ok` 168 { 169 self.get() 170 .payload 171 .serialize_yoke(&mut <dyn erased_serde::Serializer>::erase(serializer)) 172 } 173 174 /// Serializes this [`DataPayload`]'s value into a [`TokenStream`] 175 /// using its [`Bake`] implementations. 176 /// 177 /// # Examples 178 /// 179 /// ``` 180 /// use icu_provider::dynutil::UpcastDataPayload; 181 /// use icu_provider::export::*; 182 /// use icu_provider::hello_world::HelloWorldV1; 183 /// use icu_provider::prelude::*; 184 /// # use databake::quote; 185 /// # use std::collections::BTreeSet; 186 /// 187 /// // Create an example DataPayload 188 /// let payload: DataPayload<HelloWorldV1> = Default::default(); 189 /// let export: DataPayload<ExportMarker> = UpcastDataPayload::upcast(payload); 190 /// 191 /// let env = databake::CrateEnv::default(); 192 /// let tokens = export.tokenize(&env); 193 /// assert_eq!( 194 /// quote! { 195 /// icu_provider::hello_world::HelloWorld { 196 /// message: alloc::borrow::Cow::Borrowed("(und) Hello World"), 197 /// } 198 /// } 199 /// .to_string(), 200 /// tokens.to_string() 201 /// ); 202 /// assert_eq!( 203 /// env.into_iter().collect::<BTreeSet<_>>(), 204 /// ["icu_provider", "alloc"] 205 /// .into_iter() 206 /// .collect::<BTreeSet<_>>() 207 /// ); 208 /// ``` tokenize(&self, ctx: &CrateEnv) -> TokenStream209 pub fn tokenize(&self, ctx: &CrateEnv) -> TokenStream { 210 self.get().payload.bake_yoke(ctx) 211 } 212 213 /// If this payload's struct can be dereferenced as a [`VarULE`], 214 /// returns a [`TokenStream`] of the slice encoded as a [`VarZeroVec`]. tokenize_encoded_seq(structs: &[&Self], ctx: &CrateEnv) -> Option<TokenStream>215 pub fn tokenize_encoded_seq(structs: &[&Self], ctx: &CrateEnv) -> Option<TokenStream> { 216 let (first, rest) = structs.split_first()?; 217 first.get().payload.maybe_bake_varule_encoded(rest, ctx) 218 } 219 220 /// Returns the data size using postcard encoding postcard_size(&self) -> usize221 pub fn postcard_size(&self) -> usize { 222 use postcard::ser_flavors::{Flavor, Size}; 223 let mut serializer = postcard::Serializer { 224 output: Size::default(), 225 }; 226 let _infallible = self 227 .get() 228 .payload 229 .serialize_yoke(&mut <dyn erased_serde::Serializer>::erase(&mut serializer)); 230 231 serializer.output.finalize().unwrap_or_default() 232 } 233 234 /// Returns an estimate of the baked size, made up of the size of the struct itself, 235 /// as well as the sizes of all its static borrows. 236 /// 237 /// As static borrows are deduplicated by the linker, this is often overcounting. baked_size(&self) -> usize238 pub fn baked_size(&self) -> usize { 239 self.get().payload.bake_size() 240 } 241 } 242 243 impl core::hash::Hash for DataPayload<ExportMarker> { hash<H: core::hash::Hasher>(&self, state: &mut H)244 fn hash<H: core::hash::Hasher>(&self, state: &mut H) { 245 self.hash_and_postcard_size(state); 246 } 247 } 248 249 impl DataPayload<ExportMarker> { 250 /// Calculates a payload hash and the postcard size hash_and_postcard_size<H: core::hash::Hasher>(&self, state: &mut H) -> usize251 pub fn hash_and_postcard_size<H: core::hash::Hasher>(&self, state: &mut H) -> usize { 252 use postcard::ser_flavors::Flavor; 253 254 struct HashFlavor<'a, H>(&'a mut H, usize); 255 impl<H: core::hash::Hasher> Flavor for HashFlavor<'_, H> { 256 type Output = usize; 257 258 fn try_push(&mut self, data: u8) -> postcard::Result<()> { 259 self.0.write_u8(data); 260 self.1 += 1; 261 Ok(()) 262 } 263 264 fn finalize(self) -> postcard::Result<Self::Output> { 265 Ok(self.1) 266 } 267 } 268 269 let mut serializer = postcard::Serializer { 270 output: HashFlavor(state, 0), 271 }; 272 273 let _infallible = self 274 .get() 275 .payload 276 .serialize_yoke(&mut <dyn erased_serde::Serializer>::erase(&mut serializer)); 277 278 serializer.output.1 279 } 280 } 281 282 /// Marker type for [`ExportBox`]. 283 #[allow(clippy::exhaustive_structs)] // marker type 284 #[derive(Debug)] 285 pub struct ExportMarker {} 286 287 impl DynamicDataMarker for ExportMarker { 288 type DataStruct = ExportBox; 289 } 290 291 #[cfg(test)] 292 mod tests { 293 use super::*; 294 use crate::hello_world::*; 295 296 #[test] test_compare_with_dyn()297 fn test_compare_with_dyn() { 298 let payload1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld { 299 message: "abc".into(), 300 }); 301 let payload2: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld { 302 message: "abc".into(), 303 }); 304 let payload3: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld { 305 message: "def".into(), 306 }); 307 308 assert!(payload1.eq_dyn(&payload2)); 309 assert!(payload2.eq_dyn(&payload1)); 310 311 assert!(!payload1.eq_dyn(&payload3)); 312 assert!(!payload3.eq_dyn(&payload1)); 313 } 314 315 #[test] test_export_marker_partial_eq()316 fn test_export_marker_partial_eq() { 317 let payload1: DataPayload<ExportMarker> = 318 UpcastDataPayload::upcast(DataPayload::<HelloWorldV1>::from_owned(HelloWorld { 319 message: "abc".into(), 320 })); 321 let payload2: DataPayload<ExportMarker> = 322 UpcastDataPayload::upcast(DataPayload::<HelloWorldV1>::from_owned(HelloWorld { 323 message: "abc".into(), 324 })); 325 let payload3: DataPayload<ExportMarker> = 326 UpcastDataPayload::upcast(DataPayload::<HelloWorldV1>::from_owned(HelloWorld { 327 message: "def".into(), 328 })); 329 330 assert_eq!(payload1, payload2); 331 assert_eq!(payload2, payload1); 332 assert_ne!(payload1, payload3); 333 assert_ne!(payload3, payload1); 334 } 335 } 336