1mod pack_info;
2mod seq_reader;
3mod unpack_info;
4
5pub use self::seq_reader::*;
6use self::{pack_info::PackInfo, unpack_info::UnpackInfo};
7use crate::{archive::*, encoders, lzma::*, Error, SevenZArchiveEntry};
8use bit_set::BitSet;
9use byteorder::*;
10use crc32fast::Hasher;
11use std::{
12 cell::Cell,
13 fs::File,
14 io::{Read, Seek, Write},
15 path::Path,
16 rc::Rc,
17 sync::Arc,
18};
19
20macro_rules! write_times {
21 ($fn_name:tt, $nid:expr, $has_time:tt, $time:tt) => {
23 write_times!($fn_name, $nid, $has_time, $time, write_u64);
24 };
25 ($fn_name:tt, $nid:expr, $has_time:tt, $time:tt, $write_fn:tt) => {
26 fn $fn_name<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
27 let mut num = 0;
28 for entry in self.files.iter() {
29 if entry.$has_time {
30 num += 1;
31 }
32 }
33 if num > 0 {
34 header.write_u8($nid)?;
35 let mut temp: Vec<u8> = Vec::with_capacity(128);
36 let mut out = &mut temp;
37 if num != self.files.len() {
38 out.write_u8(0)?;
39 let mut times = BitSet::with_capacity(self.files.len());
40 for i in 0..self.files.len() {
41 if self.files[i].$has_time {
42 times.insert(i);
43 }
44 }
45 write_bit_set(&mut out, ×)?;
46 } else {
47 out.write_u8(1)?;
48 }
49 out.write_u8(0)?;
50 for file in self.files.iter() {
51 if file.$has_time {
52 out.$write_fn::<LittleEndian>((file.$time).into())?;
53 }
54 }
55 out.flush()?;
56 write_u64(header, temp.len() as u64)?;
57 header.write_all(&temp)?;
58 }
59 Ok(())
60 }
61 };
62}
63
64type Result<T> = std::result::Result<T, Error>;
65
66pub struct SevenZWriter<W: Write> {
68 output: W,
69 files: Vec<SevenZArchiveEntry>,
70 content_methods: Arc<Vec<SevenZMethodConfiguration>>,
71 pack_info: PackInfo,
72 unpack_info: UnpackInfo,
73 encrypt_header: bool,
74}
75
76#[cfg(not(target_arch = "wasm32"))]
77impl SevenZWriter<File> {
78 pub fn create(path: impl AsRef<Path>) -> Result<Self> {
80 let file = File::create(path.as_ref())
81 .map_err(|e| Error::file_open(e, path.as_ref().to_string_lossy().to_string()))?;
82 Self::new(file)
83 }
84}
85
86impl<W: Write + Seek> SevenZWriter<W> {
87 pub fn new(mut writer: W) -> Result<Self> {
89 writer
90 .seek(std::io::SeekFrom::Start(SIGNATURE_HEADER_SIZE))
91 .map_err(Error::io)?;
92
93 Ok(Self {
94 output: writer,
95 files: Default::default(),
96 content_methods: Arc::new(vec![SevenZMethodConfiguration::new(SevenZMethod::LZMA2)]),
97 pack_info: Default::default(),
98 unpack_info: Default::default(),
99 encrypt_header: true,
100 })
101 }
102
103 pub fn set_content_methods(
105 &mut self,
106 content_methods: Vec<SevenZMethodConfiguration>,
107 ) -> &mut Self {
108 if content_methods.is_empty() {
109 return self;
110 }
111 self.content_methods = Arc::new(content_methods);
112 self
113 }
114
115 pub fn set_encrypt_header(&mut self, enabled: bool) {
117 self.encrypt_header = enabled;
118 }
119
120 pub fn push_archive_entry<R: Read>(
139 &mut self,
140 mut entry: SevenZArchiveEntry,
141 reader: Option<R>,
142 ) -> Result<&SevenZArchiveEntry> {
143 if !entry.is_directory {
144 if let Some(mut r) = reader {
145 let mut compressed_len = 0;
146 let mut compressed = CompressWrapWriter::new(&mut self.output, &mut compressed_len);
147
148 let mut more_sizes: Vec<Rc<Cell<usize>>> =
149 Vec::with_capacity(self.content_methods.len() - 1);
150
151 let (crc, size) = {
152 let mut w = Self::create_writer(
153 &self.content_methods,
154 &mut compressed,
155 &mut more_sizes,
156 )?;
157 let mut write_len = 0;
158 let mut w = CompressWrapWriter::new(&mut w, &mut write_len);
159 let mut buf = [0u8; 4096];
160 loop {
161 match r.read(&mut buf) {
162 Ok(n) => {
163 if n == 0 {
164 break;
165 }
166 w.write_all(&buf[..n]).map_err(|e| {
167 Error::io_msg(e, format!("Encode entry:{}", entry.name()))
168 })?;
169 }
170 Err(e) => {
171 return Err(Error::io_msg(
172 e,
173 format!("Encode entry:{}", entry.name()),
174 ));
175 }
176 }
177 }
178 w.flush()
179 .map_err(|e| Error::io_msg(e, format!("Encode entry:{}", entry.name())))?;
180 w.write(&[])
181 .map_err(|e| Error::io_msg(e, format!("Encode entry:{}", entry.name())))?;
182
183 (w.crc_value(), write_len)
184 };
185 let compressed_crc = compressed.crc_value();
186 entry.has_stream = true;
187 entry.size = size as u64;
188 entry.crc = crc as u64;
189 entry.has_crc = true;
190 entry.compressed_crc = compressed_crc as u64;
191 entry.compressed_size = compressed_len as u64;
192 self.pack_info
193 .add_stream(compressed_len as u64, compressed_crc);
194
195 let mut sizes = Vec::with_capacity(more_sizes.len() + 1);
196 sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
197 sizes.push(size as u64);
198
199 self.unpack_info
200 .add(self.content_methods.clone(), sizes, crc);
201
202 self.files.push(entry);
203 return Ok(self.files.last().unwrap());
204 }
205 }
206 entry.has_stream = false;
207 entry.size = 0;
208 entry.compressed_size = 0;
209 entry.has_crc = false;
210 self.files.push(entry);
211 Ok(self.files.last().unwrap())
212 }
213
214 pub fn push_archive_entries<R: Read>(
219 &mut self,
220 mut entries: Vec<SevenZArchiveEntry>,
221 reader: SeqReader<SourceReader<R>>,
222 ) -> Result<&mut Self> {
223 let mut r = reader;
224 assert_eq!(r.reader_len(), entries.len());
225 let mut compressed_len = 0;
226 let mut compressed = CompressWrapWriter::new(&mut self.output, &mut compressed_len);
227 let content_methods = &self.content_methods;
228 let mut more_sizes: Vec<Rc<Cell<usize>>> = Vec::with_capacity(content_methods.len() - 1);
229
230 let (crc, size) = {
231 let mut w = Self::create_writer(content_methods, &mut compressed, &mut more_sizes)?;
232 let mut write_len = 0;
233 let mut w = CompressWrapWriter::new(&mut w, &mut write_len);
234 let mut buf = [0u8; 4096];
235 fn entries_names(entries: &[SevenZArchiveEntry]) -> String {
236 let mut names = String::with_capacity(512);
237 for ele in entries.iter() {
238 names.push_str(&ele.name);
239 names.push(';');
240 if names.len() > 512 {
241 break;
242 }
243 }
244 names
245 }
246 loop {
247 match r.read(&mut buf) {
248 Ok(n) => {
249 if n == 0 {
250 break;
251 }
252 w.write_all(&buf[..n]).map_err(|e| {
253 Error::io_msg(e, format!("Encode entries:{}", entries_names(&entries)))
254 })?;
255 }
256 Err(e) => {
257 return Err(Error::io_msg(
258 e,
259 format!("Encode entries:{}", entries_names(&entries)),
260 ));
261 }
262 }
263 }
264 w.flush().map_err(|e| {
265 let mut names = String::with_capacity(512);
266 for ele in entries.iter() {
267 names.push_str(&ele.name);
268 names.push(';');
269 if names.len() > 512 {
270 break;
271 }
272 }
273 Error::io_msg(e, format!("Encode entry:{}", names))
274 })?;
275 w.write(&[]).map_err(|e| {
276 Error::io_msg(e, format!("Encode entry:{}", entries_names(&entries)))
277 })?;
278
279 (w.crc_value(), write_len)
280 };
281 let compressed_crc = compressed.crc_value();
282 let mut sub_stream_crcs = Vec::with_capacity(entries.len());
283 let mut sub_stream_sizes = Vec::with_capacity(entries.len());
284 for i in 0..entries.len() {
285 let entry = &mut entries[i];
286 let ri = &r[i];
287 entry.crc = ri.crc_value() as u64;
288 entry.size = ri.read_count() as u64;
289 sub_stream_crcs.push(entry.crc as u32);
290 sub_stream_sizes.push(entry.size);
291 entry.has_crc = true;
292 }
293
294 self.pack_info
295 .add_stream(compressed_len as u64, compressed_crc);
296
297 let mut sizes = Vec::with_capacity(more_sizes.len() + 1);
298 sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
299 sizes.push(size as u64);
300
301 self.unpack_info.add_multiple(
302 content_methods.clone(),
303 sizes,
304 crc,
305 entries.len() as u64,
306 sub_stream_sizes,
307 sub_stream_crcs,
308 );
309
310 self.files.extend(entries);
311 Ok(self)
312 }
313
314 fn create_writer<'a, O: Write + 'a>(
315 methods: &[SevenZMethodConfiguration],
316 out: O,
317 more_sized: &mut Vec<Rc<Cell<usize>>>,
318 ) -> Result<Box<dyn Write + 'a>> {
319 let mut encoder: Box<dyn Write> = Box::new(out);
320 let mut first = true;
321 for mc in methods.iter() {
322 if !first {
323 let counting = CountingWriter::new(encoder);
324 more_sized.push(counting.counting());
325 encoder = Box::new(encoders::add_encoder(counting, mc)?);
326 } else {
327 let counting = CountingWriter::new(encoder);
328 encoder = Box::new(encoders::add_encoder(counting, mc)?);
329 }
330 first = false;
331 }
332 Ok(encoder)
333 }
334
335 pub fn finish(mut self) -> std::io::Result<W> {
337 let mut header: Vec<u8> = Vec::with_capacity(64 * 1024);
338 self.write_encoded_header(&mut header)?;
339 let header_pos = self.output.stream_position()?;
340 self.output.write_all(&header)?;
341 let crc32 = crc32fast::hash(&header);
342 let mut hh = [0u8; SIGNATURE_HEADER_SIZE as usize];
343 {
344 let mut hhw = hh.as_mut_slice();
345 hhw.write_all(SEVEN_Z_SIGNATURE)?;
347 hhw.write_u8(0)?;
349 hhw.write_u8(2)?;
350 hhw.write_u32::<LittleEndian>(0)?;
352
353 hhw.write_u64::<LittleEndian>(header_pos - SIGNATURE_HEADER_SIZE)?;
355 hhw.write_u64::<LittleEndian>(0xffffffff & header.len() as u64)?;
356 hhw.write_u32::<LittleEndian>(crc32)?;
357 }
358 let crc32 = crc32fast::hash(&hh[12..]);
359 hh[8..12].copy_from_slice(&crc32.to_le_bytes());
360
361 self.output.seek(std::io::SeekFrom::Start(0))?;
362 self.output.write_all(&hh)?;
363 Ok(self.output)
364 }
365
366 fn write_header<H: Write>(&mut self, header: &mut H) -> std::io::Result<()> {
367 header.write_u8(K_HEADER)?;
368 header.write_u8(K_MAIN_STREAMS_INFO)?;
369 self.write_streams_info(header)?;
370 self.write_files_info(header)?;
371 header.write_u8(K_END)?;
372 Ok(())
373 }
374
375 fn write_encoded_header<H: Write>(&mut self, header: &mut H) -> std::io::Result<()> {
376 let mut raw_header = Vec::with_capacity(64 * 1024);
377 self.write_header(&mut raw_header)?;
378 let mut pack_info = PackInfo::default();
379
380 let position = self.output.stream_position()?;
381 let pos = position - SIGNATURE_HEADER_SIZE;
382 pack_info.pos = pos;
383
384 let mut more_sizes = vec![];
385 let size = raw_header.len() as u64;
386 let crc32 = crc32fast::hash(&raw_header);
387 let mut methods = vec![];
388
389 if self.encrypt_header {
390 for conf in self.content_methods.iter() {
391 if conf.method.id() == SevenZMethod::AES256SHA256.id() {
392 methods.push(conf.clone());
393 break;
394 }
395 }
396 }
397
398 methods.push(SevenZMethodConfiguration::new(SevenZMethod::LZMA));
399
400 let methods = Arc::new(methods);
401
402 let mut encoded_data = Vec::with_capacity(size as usize / 2);
403
404 let mut compress_size = 0;
405 let mut compressed = CompressWrapWriter::new(&mut encoded_data, &mut compress_size);
406 {
407 let mut encoder = Self::create_writer(&methods, &mut compressed, &mut more_sizes)
408 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
409 encoder.write_all(&raw_header)?;
410 let _ = encoder.write(&[])?;
411 }
412
413 let compress_crc = compressed.crc_value();
414 let compress_size = *compressed.bytes_written;
415 if compress_size as u64 + 20 >= size {
416 header.write_all(&raw_header)?;
418 return Ok(());
419 }
420 self.output.write_all(&encoded_data[..compress_size])?;
421
422 pack_info.add_stream(compress_size as u64, compress_crc);
423
424 let mut unpack_info = UnpackInfo::default();
425 let mut sizes = Vec::with_capacity(1 + more_sizes.len());
426 sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
427 sizes.push(size);
428 unpack_info.add(methods, sizes, crc32);
429
430 header.write_u8(K_ENCODED_HEADER)?;
431
432 pack_info.write_to(header)?;
433 unpack_info.write_to(header)?;
434 unpack_info.write_substreams(header)?;
435
436 header.write_u8(K_END)?;
437
438 Ok(())
439 }
440
441 fn write_streams_info<H: Write>(&mut self, header: &mut H) -> std::io::Result<()> {
442 if self.pack_info.len() > 0 {
443 self.pack_info.write_to(header)?;
444 self.unpack_info.write_to(header)?;
445 }
446 self.unpack_info.write_substreams(header)?;
447
448 header.write_u8(K_END)?;
449 Ok(())
450 }
451
452 fn write_files_info<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
453 header.write_u8(K_FILES_INFO)?;
454 write_u64(header, self.files.len() as u64)?;
455 self.write_file_empty_streams(header)?;
456 self.write_file_empty_files(header)?;
457 self.write_file_anti_items(header)?;
458 self.write_file_names(header)?;
459 self.write_file_ctimes(header)?;
460 self.write_file_atimes(header)?;
461 self.write_file_mtimes(header)?;
462 self.write_file_windows_attrs(header)?;
463 header.write_u8(K_END)?;
464 Ok(())
465 }
466
467 fn write_file_empty_streams<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
468 let mut has_empty = false;
469 for entry in self.files.iter() {
470 if !entry.has_stream {
471 has_empty = true;
472 break;
473 }
474 }
475 if has_empty {
476 header.write_u8(K_EMPTY_STREAM)?;
477 let mut bitset = BitSet::with_capacity(self.files.len());
478 for (i, entry) in self.files.iter().enumerate() {
479 if !entry.has_stream {
480 bitset.insert(i);
481 }
482 }
483 let mut temp: Vec<u8> = Vec::with_capacity(bitset.len() / 8 + 1);
484 write_bit_set(&mut temp, &bitset)?;
485 write_u64(header, temp.len() as u64)?;
486 header.write_all(temp.as_slice())?;
487 }
488 Ok(())
489 }
490
491 fn write_file_empty_files<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
492 let mut has_empty = false;
493 let mut empty_stream_counter = 0;
494 let mut bitset = BitSet::new();
495 for entry in self.files.iter() {
496 if !entry.has_stream {
497 let is_dir = entry.is_directory();
498 has_empty |= !is_dir;
499 if !is_dir {
500 bitset.insert(empty_stream_counter);
501 }
502 empty_stream_counter += 1;
503 }
504 }
505 if has_empty {
506 header.write_u8(K_EMPTY_FILE)?;
507
508 let mut temp: Vec<u8> = Vec::with_capacity(bitset.len() / 8 + 1);
509 write_bit_set(&mut temp, &bitset)?;
510 write_u64(header, temp.len() as u64)?;
511 header.write_all(&temp)?;
512 }
513 Ok(())
514 }
515
516 fn write_file_anti_items<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
517 let mut has_anti = false;
518 let mut counter = 0;
519 let mut bitset = BitSet::new();
520 for entry in self.files.iter() {
521 if !entry.has_stream {
522 let is_anti = entry.is_anti_item();
523 has_anti |= !is_anti;
524 if !is_anti {
525 bitset.insert(counter);
526 }
527 counter += 1;
528 }
529 }
530 if has_anti {
531 header.write_u8(K_ANTI)?;
532
533 let mut temp: Vec<u8> = Vec::with_capacity(bitset.len() / 8 + 1);
534 write_bit_set(&mut temp, &bitset)?;
535 write_u64(header, temp.len() as u64)?;
536 header.write_all(temp.as_slice())?;
537 }
538 Ok(())
539 }
540
541 fn write_file_names<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
542 header.write_u8(K_NAME)?;
543 let mut temp: Vec<u8> = Vec::with_capacity(128);
544 let out = &mut temp;
545 out.write_u8(0)?;
546 for file in self.files.iter() {
547 for c in file.name().encode_utf16() {
548 let buf = c.to_le_bytes();
549 out.write_all(&buf)?;
550 }
551 out.write_all(&[0u8; 2])?;
552 }
553 write_u64(header, temp.len() as u64)?;
554 header.write_all(temp.as_slice())?;
555 Ok(())
556 }
557
558 write_times!(
559 write_file_ctimes,
560 K_C_TIME,
561 has_creation_date,
562 creation_date
563 );
564 write_times!(write_file_atimes, K_A_TIME, has_access_date, access_date);
565 write_times!(
566 write_file_mtimes,
567 K_M_TIME,
568 has_last_modified_date,
569 last_modified_date
570 );
571 write_times!(
572 write_file_windows_attrs,
573 K_WIN_ATTRIBUTES,
574 has_windows_attributes,
575 windows_attributes,
576 write_u32
577 );
578}
579
580pub(crate) fn write_u64<W: Write>(header: &mut W, mut value: u64) -> std::io::Result<()> {
581 let mut first = 0;
582 let mut mask = 0x80;
583 let mut i = 0;
584 while i < 8 {
585 if value < (1u64 << (7 * (i + 1))) {
586 first |= value >> (8 * i);
587 break;
588 }
589 first |= mask;
590 mask >>= 1;
591 i += 1;
592 }
593 header.write_u8((first & 0xff) as u8)?;
594 while i > 0 {
595 header.write_u8((value & 0xff) as u8)?;
596 value >>= 8;
597 i -= 1;
598 }
599 Ok(())
600}
601
602fn write_bit_set<W: Write>(mut write: W, bs: &BitSet) -> std::io::Result<()> {
603 let mut cache = 0;
604 let mut shift = 7;
605 for i in 0..bs.get_ref().len() {
606 let set = if bs.contains(i) { 1 } else { 0 };
607 cache |= set << shift;
608 shift -= 1;
609 if shift < 0 {
610 write.write_u8(cache)?;
611 shift = 7;
612 cache = 0;
613 }
614 }
615 if shift != 7 {
616 write.write_u8(cache)?;
617 }
618 Ok(())
619}
620
621struct CompressWrapWriter<'a, W> {
622 writer: W,
623 crc: Hasher,
624 cache: Vec<u8>,
625 bytes_written: &'a mut usize,
626}
627
628impl<'a, W: Write> CompressWrapWriter<'a, W> {
629 pub fn new(writer: W, bytes_written: &'a mut usize) -> Self {
630 Self {
631 writer,
632 crc: Hasher::new(),
633 cache: Vec::with_capacity(8192),
634 bytes_written,
635 }
636 }
637
638 pub fn crc_value(&mut self) -> u32 {
639 let crc = std::mem::replace(&mut self.crc, Hasher::new());
640 crc.finalize()
641 }
642}
643
644impl<W: Write> Write for CompressWrapWriter<'_, W> {
645 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
646 self.cache.resize(buf.len(), Default::default());
647 let len = self.writer.write(buf)?;
648 self.crc.update(&buf[..len]);
649 *self.bytes_written += len;
650 Ok(len)
651 }
652
653 fn flush(&mut self) -> std::io::Result<()> {
654 self.writer.flush()
655 }
656}