diff --git a/provider/blob/src/export/blob_exporter.rs b/provider/blob/src/export/blob_exporter.rs index e2b58976f55..aadf31cec33 100644 --- a/provider/blob/src/export/blob_exporter.rs +++ b/provider/blob/src/export/blob_exporter.rs @@ -7,31 +7,34 @@ use icu_provider::export::DataExporter; use icu_provider::iter::IterableDynProvider; use icu_provider::prelude::*; use icu_provider::serde::SerializeMarker; -use litemap::LiteMap; use writeable::Writeable; use zerovec::map2d::ZeroMap2d; +use std::sync::Mutex; /// A data exporter that writes data to a single-file blob. /// See the module-level docs for an example. pub struct BlobExporter { - resources: LiteMap<(ResourceKeyHash, String), Vec>, - sink: Box, + resources: Mutex)>>, + sink: Mutex>>, } +unsafe impl Sync for BlobExporter {} +unsafe impl Send for BlobExporter {} + impl BlobExporter { /// Create a [`BlobExporter`] that writes to the given I/O stream. pub fn new_with_sink(sink: Box) -> Self { Self { - resources: LiteMap::new(), - sink, + resources: Mutex::new(Vec::new()), + sink: Mutex::new(Some(sink)), } } } impl Drop for BlobExporter { fn drop(&mut self) { - if !self.resources.is_empty() { - panic!("Please call close before dropping FilesystemExporter"); + if self.sink.lock().unwrap().is_some() { + panic!("Please call close before dropping BlobExporter"); } } } @@ -52,34 +55,31 @@ impl DataExporter for BlobExporter { .load_payload(key, req)? .take_payload()? .serialize(&mut ::erase(&mut serializer))?; - self.resources.insert( - ( - key.get_hash(), - req.options.writeable_to_string().into_owned(), - ), + self.resources.lock().unwrap().push(( + key.get_hash(), + req.options.writeable_to_string().into_owned(), serializer.output.0, - ); + )); } Ok(()) } fn close(&mut self) -> Result<(), DataError> { - // Convert from LiteMap<(String, String), Vec> to ZeroMap2d - let mut zm: ZeroMap2d = - ZeroMap2d::with_capacity(self.resources.len()); - for ((key, option), bytes) in self.resources.iter() { - zm.insert(key, option, bytes); + if let Some(mut sink) = self.sink.lock().unwrap().take() { + // We are the first `close` and own the sink now + let zm = self.resources.lock().unwrap().drain(..).collect::>(); + let blob = BlobSchema::V001(BlobSchemaV1 { + resources: zm.as_borrowed(), + }); + log::info!("Serializing blob to output stream..."); + let mut serializer = postcard::Serializer { + output: postcard::flavors::AllocVec(Vec::new()), + }; + serde::Serialize::serialize(&blob, &mut serializer)?; + sink.write_all(&serializer.output.0)?; + Ok(()) + } else { + Err(DataError::custom("Close was called twice")) } - let blob = BlobSchema::V001(BlobSchemaV1 { - resources: zm.as_borrowed(), - }); - log::info!("Serializing blob to output stream..."); - let mut serializer = postcard::Serializer { - output: postcard::flavors::AllocVec(Vec::new()), - }; - serde::Serialize::serialize(&blob, &mut serializer)?; - self.sink.write_all(&serializer.output.0)?; - self.resources.clear(); - Ok(()) } } diff --git a/provider/fs/src/export/fs_exporter.rs b/provider/fs/src/export/fs_exporter.rs index 0eeea8277dd..b3c3b9f6d86 100644 --- a/provider/fs/src/export/fs_exporter.rs +++ b/provider/fs/src/export/fs_exporter.rs @@ -59,6 +59,9 @@ pub struct FilesystemExporter { serializer: Box, } +unsafe impl Sync for FilesystemExporter {} +unsafe impl Send for FilesystemExporter {} + impl DataExporter for FilesystemExporter { fn put_key_with_options( &mut self, diff --git a/provider/testdata/data/testdata.postcard b/provider/testdata/data/testdata.postcard index b754b062049..43546afd81d 100644 Binary files a/provider/testdata/data/testdata.postcard and b/provider/testdata/data/testdata.postcard differ