1use super::layers::traits::InnerWriterTrait;
2use super::{ArchiveFileBlock, ArchiveFileID, ArchiveReader, ArchiveWriter, Error};
4use std::collections::HashMap;
5use std::hash::BuildHasher;
6use std::io::{self, Read, Seek, Write};
7
8pub fn linear_extract<W1: InnerWriterTrait, R: Read + Seek, S: BuildHasher>(
23 archive: &mut ArchiveReader<R>,
24 export: &mut HashMap<&String, W1, S>,
25) -> Result<(), Error> {
26 archive.src.rewind()?;
28
29 let mut src = io::BufReader::new(&mut archive.src);
32
33 let mut id2filename: HashMap<ArchiveFileID, String> = HashMap::new();
36
37 'read_block: loop {
38 match ArchiveFileBlock::from(&mut src)? {
39 ArchiveFileBlock::FileStart { filename, id } => {
40 if export.contains_key(&filename) {
43 id2filename.insert(id, filename.clone());
44 }
45 }
46 ArchiveFileBlock::EndOfFile { id, .. } => {
47 id2filename.remove(&id);
49 }
50 ArchiveFileBlock::FileContent { length, id, .. } => {
51 let copy_src = &mut (&mut src).take(length);
54 let mut extracted: bool = false;
56 if let Some(fname) = id2filename.get(&id) {
57 if let Some(writer) = export.get_mut(fname) {
58 io::copy(copy_src, writer)?;
59 extracted = true;
60 }
61 };
62 if !extracted {
63 io::copy(copy_src, &mut io::sink())?;
65 }
66 }
67 ArchiveFileBlock::EndOfArchiveData {} => {
68 break 'read_block;
70 }
71 }
72 }
73 Ok(())
74}
75
76pub struct StreamWriter<'a, 'b, W: InnerWriterTrait> {
82 archive: &'b mut ArchiveWriter<'a, W>,
83 file_id: ArchiveFileID,
84}
85
86impl<'a, 'b, W: InnerWriterTrait> StreamWriter<'a, 'b, W> {
87 pub fn new(archive: &'b mut ArchiveWriter<'a, W>, file_id: ArchiveFileID) -> Self {
88 Self { archive, file_id }
89 }
90}
91
92impl<'a, 'b, W: InnerWriterTrait> Write for StreamWriter<'a, 'b, W> {
93 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
94 self.archive
95 .append_file_content(self.file_id, buf.len() as u64, buf)?;
96 Ok(buf.len())
97 }
98
99 fn flush(&mut self) -> io::Result<()> {
100 self.archive.flush()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use rand::distributions::Standard;
107 use rand::prelude::Distribution;
108 use rand::{RngCore, SeedableRng};
109 use rand_chacha::ChaChaRng;
110 use x25519_dalek::{PublicKey, StaticSecret};
111
112 use super::*;
113 use crate::tests::build_archive;
114 use crate::*;
115 use std::io::Cursor;
116
117 const UNCOMPRESSED_DATA_SIZE: u32 = 4 * 1024 * 1024;
119
120 #[test]
121 fn full_linear_extract() {
122 let (mla, key, files) = build_archive(None, false);
124
125 let dest = Cursor::new(mla.into_raw());
127 let mut config = ArchiveReaderConfig::new();
128 config.add_private_keys(std::slice::from_ref(&key));
129 let mut mla_read = ArchiveReader::from_config(dest, config).unwrap();
130
131 let file_list: Vec<String> = mla_read
133 .list_files()
134 .expect("reader.list_files")
135 .cloned()
136 .collect();
137 let mut export: HashMap<&String, Vec<u8>> =
138 file_list.iter().map(|fname| (fname, Vec::new())).collect();
139 linear_extract(&mut mla_read, &mut export).expect("Extract error");
140
141 for (fname, content) in files.iter() {
143 assert_eq!(export.get(fname).unwrap(), content);
144 }
145 }
146
147 #[test]
148 fn one_linear_extract() {
149 let (mla, key, files) = build_archive(None, false);
151
152 let dest = Cursor::new(mla.into_raw());
154 let mut config = ArchiveReaderConfig::new();
155 config.add_private_keys(std::slice::from_ref(&key));
156 let mut mla_read = ArchiveReader::from_config(dest, config).unwrap();
157
158 let mut export: HashMap<&String, Vec<u8>> = HashMap::new();
160 export.insert(&files[0].0, Vec::new());
161 linear_extract(&mut mla_read, &mut export).expect("Extract error");
162
163 assert_eq!(export.get(&files[0].0).unwrap(), &files[0].1);
165 }
166
167 #[test]
168 fn linear_extract_big_file() {
176 let file_length = 4 * UNCOMPRESSED_DATA_SIZE as usize;
177
178 let file = Vec::new();
180 let mut rng = ChaChaRng::seed_from_u64(0);
182 let mut bytes = [0u8; 32];
183 rng.fill_bytes(&mut bytes);
184 let key = StaticSecret::from(bytes);
185 let mut config = ArchiveWriterConfig::new();
186 let layers = Layers::ENCRYPT | Layers::COMPRESS;
187 config
188 .set_layers(layers)
189 .add_public_keys(&[PublicKey::from(&key)]);
190 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
191
192 let fname = "my_file".to_string();
193 let data: Vec<u8> = Standard.sample_iter(&mut rng).take(file_length).collect();
194 assert_eq!(data.len(), file_length);
195 mla.add_file(&fname, data.len() as u64, data.as_slice())
196 .unwrap();
197
198 mla.finalize().unwrap();
199
200 let dest = Cursor::new(mla.into_raw());
204 let mut config = ArchiveReaderConfig::new();
205 config.add_private_keys(std::slice::from_ref(&key));
206 let mut mla_read = ArchiveReader::from_config(dest, config).unwrap();
207
208 let mut export: HashMap<&String, Vec<u8>> = HashMap::new();
210 export.insert(&fname, Vec::new());
211 linear_extract(&mut mla_read, &mut export).expect("Extract error");
212
213 assert_eq!(export.get(&fname).unwrap(), &data);
215 }
216
217 #[test]
218 fn stream_writer() {
219 let file = Vec::new();
220 let mut mla = ArchiveWriter::from_config(file, ArchiveWriterConfig::new())
221 .expect("Writer init failed");
222
223 let fake_file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
224
225 let id = mla.start_file("my_file").unwrap();
227 let mut sw = StreamWriter::new(&mut mla, id);
228 sw.write_all(&fake_file[..5]).unwrap();
229 sw.write_all(&fake_file[5..]).unwrap();
230 mla.end_file(id).unwrap();
231
232 let id = mla.start_file("my_file2").unwrap();
234 let mut sw = StreamWriter::new(&mut mla, id);
235 assert_eq!(
236 io::copy(&mut fake_file.as_slice(), &mut sw).unwrap(),
237 fake_file.len() as u64
238 );
239 mla.end_file(id).unwrap();
240
241 mla.finalize().unwrap();
242
243 let dest = mla.into_raw();
245 let buf = Cursor::new(dest.as_slice());
246 let mut mla_read = ArchiveReader::from_config(buf, ArchiveReaderConfig::new()).unwrap();
247 let mut content1 = Vec::new();
248 mla_read
249 .get_file("my_file".to_string())
250 .unwrap()
251 .unwrap()
252 .data
253 .read_to_end(&mut content1)
254 .unwrap();
255 assert_eq!(content1.as_slice(), fake_file.as_slice());
256 let mut content2 = Vec::new();
257 mla_read
258 .get_file("my_file2".to_string())
259 .unwrap()
260 .unwrap()
261 .data
262 .read_to_end(&mut content2)
263 .unwrap();
264 assert_eq!(content2.as_slice(), fake_file.as_slice());
265 }
266}