#![allow(clippy::expect_used)]
use crate::blob_schema::*;
use icu_provider::datagen::*;
use icu_provider::prelude::*;
use std::collections::HashMap;
use std::sync::Mutex;
use writeable::Writeable;
use zerovec::ule::VarULE;
use zerovec::vecs::Index32;
use zerovec::VarZeroVec;
use zerovec::ZeroMap2d;
use postcard::ser_flavors::{AllocVec, Flavor};
pub struct BlobExporter<'w> {
#[allow(clippy::type_complexity)]
resources: Mutex<Vec<(DataKeyHash, Vec<u8>, usize)>>,
unique_resources: Mutex<HashMap<Vec<u8>, usize>>,
sink: Box<dyn std::io::Write + Sync + 'w>,
}
impl<'w> BlobExporter<'w> {
pub fn new_with_sink(sink: Box<dyn std::io::Write + Sync + 'w>) -> Self {
Self {
resources: Mutex::new(Vec::new()),
unique_resources: Mutex::new(HashMap::new()),
sink,
}
}
}
impl DataExporter for BlobExporter<'_> {
fn put_payload(
&self,
key: DataKey,
locale: &DataLocale,
payload: &DataPayload<ExportMarker>,
) -> Result<(), DataError> {
log::trace!("Adding: {}/{}", key, locale);
let mut serializer = postcard::Serializer {
output: AllocVec::new(),
};
payload.serialize(&mut serializer)?;
let output = serializer
.output
.finalize()
.expect("Failed to finalize serializer output");
let idx = {
let mut unique_resources = self.unique_resources.lock().expect("poison");
let len = unique_resources.len();
*unique_resources.entry(output).or_insert(len)
};
#[allow(clippy::expect_used)]
self.resources.lock().expect("poison").push((
key.hashed(),
locale.write_to_string().into_owned().into_bytes(),
idx,
));
Ok(())
}
fn close(&mut self) -> Result<(), DataError> {
let sorted: Vec<(Vec<u8>, usize)> = {
let mut unique_resources = self.unique_resources.lock().expect("poison");
let mut sorted: Vec<(Vec<u8>, usize)> = unique_resources.drain().collect();
sorted.sort();
sorted
};
let remap: HashMap<usize, usize> = sorted
.iter()
.enumerate()
.map(|(new_id, (_, old_id))| (*old_id, new_id))
.collect();
let zm = self
.resources
.get_mut()
.expect("poison")
.iter()
.map(|(hash, locale, old_id)| {
(
hash,
Index32U8::parse_byte_slice(locale)
.expect("[u8] to IndexU32U8 should never fail"),
remap.get(old_id).expect("in-bound index"),
)
})
.collect::<ZeroMap2d<_, _, _>>();
let vzv: VarZeroVec<[u8], Index32> = {
let buffers: Vec<Vec<u8>> = sorted.into_iter().map(|(blob, _)| blob).collect();
buffers.as_slice().into()
};
if !zm.is_empty() {
let blob = BlobSchema::V001(BlobSchemaV1 {
keys: zm.as_borrowed(),
buffers: &vzv,
});
log::info!("Serializing blob to output stream...");
let output = postcard::to_allocvec(&blob)?;
self.sink.write_all(&output)?;
}
Ok(())
}
}