Skip to main content

sevenz_rust2/
writer.rs

1mod counting_writer;
2#[cfg(all(feature = "util", not(target_arch = "wasm32")))]
3mod lazy_file_reader;
4mod pack_info;
5mod seq_reader;
6mod source_reader;
7mod unpack_info;
8
9use std::{
10    cell::Cell,
11    io::{Read, Seek, Write},
12    rc::Rc,
13    sync::Arc,
14};
15#[cfg(not(target_arch = "wasm32"))]
16use std::{fs::File, path::Path};
17
18pub(crate) use counting_writer::CountingWriter;
19use crc32fast::Hasher;
20
21#[cfg(all(feature = "util", not(target_arch = "wasm32")))]
22pub(crate) use self::lazy_file_reader::LazyFileReader;
23pub(crate) use self::seq_reader::SeqReader;
24pub use self::source_reader::SourceReader;
25use self::{pack_info::PackInfo, unpack_info::UnpackInfo};
26use crate::{
27    ArchiveEntry, AutoFinish, AutoFinisher, ByteWriter, Error,
28    archive::*,
29    bitset::{BitSet, write_bit_set},
30    encoder,
31};
32
33macro_rules! write_times {
34    //write_i64
35    ($fn_name:tt, $nid:expr, $has_time:tt, $time:tt) => {
36        write_times!($fn_name, $nid, $has_time, $time, write_u64);
37    };
38    ($fn_name:tt, $nid:expr, $has_time:tt, $time:tt, $write_fn:tt) => {
39        fn $fn_name<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
40            let mut num = 0;
41            for entry in self.files.iter() {
42                if entry.$has_time {
43                    num += 1;
44                }
45            }
46            if num > 0 {
47                header.write_u8($nid)?;
48                let mut temp: Vec<u8> = Vec::with_capacity(128);
49                let mut out = &mut temp;
50                if num != self.files.len() {
51                    out.write_u8(0)?;
52                    let mut times = BitSet::with_capacity(self.files.len());
53                    for i in 0..self.files.len() {
54                        if self.files[i].$has_time {
55                            times.insert(i);
56                        }
57                    }
58                    write_bit_set(&mut out, &times)?;
59                } else {
60                    out.write_u8(1)?;
61                }
62                out.write_u8(0)?;
63                for file in self.files.iter() {
64                    if file.$has_time {
65                        out.$write_fn((file.$time).into())?;
66                    }
67                }
68                out.flush()?;
69                write_u64(header, temp.len() as u64)?;
70                header.write_all(&temp)?;
71            }
72            Ok(())
73        }
74    };
75}
76
77type Result<T> = std::result::Result<T, Error>;
78
79/// Writes a 7z archive file.
80pub struct ArchiveWriter<W: Write> {
81    output: W,
82    files: Vec<ArchiveEntry>,
83    content_methods: Arc<Vec<EncoderConfiguration>>,
84    pack_info: PackInfo,
85    unpack_info: UnpackInfo,
86    encrypt_header: bool,
87}
88
89#[cfg(not(target_arch = "wasm32"))]
90impl ArchiveWriter<File> {
91    /// Creates a file to write a 7z archive to.
92    pub fn create(path: impl AsRef<Path>) -> Result<Self> {
93        let file = File::create(path.as_ref())
94            .map_err(|e| Error::file_open(e, path.as_ref().to_string_lossy().to_string()))?;
95        Self::new(file)
96    }
97}
98
99/// Names of the entries in a block, for an error message. Truncated at ~512 bytes, since a solid
100/// block can hold many thousands.
101fn entries_names(entries: &[ArchiveEntry]) -> String {
102    let mut names = String::with_capacity(512);
103    for ele in entries.iter() {
104        names.push_str(&ele.name);
105        names.push(';');
106        if names.len() > 512 {
107            break;
108        }
109    }
110    names
111}
112
113impl<W: Write + Seek> ArchiveWriter<W> {
114    /// Prepares writer to write a 7z archive to.
115    pub fn new(mut writer: W) -> Result<Self> {
116        writer.seek(std::io::SeekFrom::Start(SIGNATURE_HEADER_SIZE))?;
117
118        Ok(Self {
119            output: writer,
120            files: Default::default(),
121            content_methods: Arc::new(vec![EncoderConfiguration::new(EncoderMethod::LZMA2)]),
122            pack_info: Default::default(),
123            unpack_info: Default::default(),
124            encrypt_header: true,
125        })
126    }
127
128    /// Returns a wrapper around `self` that will finish the stream on drop.
129    pub fn auto_finish(self) -> AutoFinisher<Self> {
130        AutoFinisher(Some(self))
131    }
132
133    /// Sets the default compression methods to use for entry data. Default is LZMA2.
134    pub fn set_content_methods(&mut self, content_methods: Vec<EncoderConfiguration>) -> &mut Self {
135        if content_methods.is_empty() {
136            return self;
137        }
138        self.content_methods = Arc::new(content_methods);
139        self
140    }
141
142    /// Whether to enable the encryption of the -header. Default is `true`.
143    pub fn set_encrypt_header(&mut self, enabled: bool) {
144        self.encrypt_header = enabled;
145    }
146
147    /// Non-solid compression - Adds an archive `entry` with data from `reader`.
148    ///
149    /// # Example
150    /// ```no_run
151    /// use std::{fs::File, path::Path};
152    ///
153    /// use sevenz_rust2::*;
154    /// let mut sz = ArchiveWriter::create("path/to/dest.7z").expect("create writer ok");
155    /// let src = Path::new("path/to/source.txt");
156    /// let name = "source.txt".to_string();
157    /// let entry = sz
158    ///     .push_archive_entry(
159    ///         ArchiveEntry::from_path(&src, name),
160    ///         Some(File::open(src).unwrap()),
161    ///     )
162    ///     .expect("ok");
163    /// let compressed_size = entry.compressed_size;
164    /// sz.finish().expect("done");
165    /// ```
166    pub fn push_archive_entry<R: Read>(
167        &mut self,
168        mut entry: ArchiveEntry,
169        reader: Option<R>,
170    ) -> Result<&ArchiveEntry> {
171        if !entry.is_directory
172            && let Some(mut r) = reader
173        {
174            let mut compressed_len = 0;
175            let mut compressed = CompressWrapWriter::new(&mut self.output, &mut compressed_len);
176
177            let mut more_sizes: Vec<Rc<Cell<usize>>> =
178                Vec::with_capacity(self.content_methods.len() - 1);
179
180            let (crc, size) = {
181                let mut w =
182                    Self::create_writer(&self.content_methods, &mut compressed, &mut more_sizes)?;
183                let mut write_len = 0;
184                let mut w = CompressWrapWriter::new(&mut w, &mut write_len);
185                let mut buf = [0u8; 4096];
186                loop {
187                    match r.read(&mut buf) {
188                        Ok(n) => {
189                            if n == 0 {
190                                break;
191                            }
192                            w.write_all(&buf[..n]).map_err(|e| {
193                                Error::io_msg(e, format!("Encode entry:{}", entry.name()))
194                            })?;
195                        }
196                        Err(e) => {
197                            return Err(Error::io_msg(e, format!("Encode entry:{}", entry.name())));
198                        }
199                    }
200                }
201                w.flush()
202                    .map_err(|e| Error::io_msg(e, format!("Encode entry:{}", entry.name())))?;
203                w.write(&[])
204                    .map_err(|e| Error::io_msg(e, format!("Encode entry:{}", entry.name())))?;
205
206                (w.crc_value(), write_len)
207            };
208            let compressed_crc = compressed.crc_value();
209            entry.has_stream = true;
210            entry.size = size as u64;
211            entry.crc = crc as u64;
212            entry.has_crc = true;
213            entry.compressed_crc = compressed_crc as u64;
214            entry.compressed_size = compressed_len as u64;
215            self.pack_info
216                .add_stream(compressed_len as u64, Some(compressed_crc));
217
218            let mut sizes = Vec::with_capacity(more_sizes.len() + 1);
219            sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
220            sizes.push(size as u64);
221
222            self.unpack_info
223                .add(self.content_methods.clone(), sizes, crc);
224
225            self.files.push(entry);
226            return Ok(self.files.last().unwrap());
227        }
228        entry.has_stream = false;
229        entry.size = 0;
230        entry.compressed_size = 0;
231        entry.has_crc = false;
232        self.files.push(entry);
233        Ok(self.files.last().unwrap())
234    }
235
236    /// Append a block compressed elsewhere by [`prepare_block`].
237    ///
238    /// Only the parts that must happen in output order: write the bytes, record the pack and block
239    /// metadata, take the entries. Everything expensive already happened on whatever thread built
240    /// the [`PreparedBlock`].
241    ///
242    /// A block holding no entries is dropped rather than written, so an empty batch does not put a
243    /// junk pack stream and a zero-substream block into the archive.
244    pub fn push_prepared_block(&mut self, block: PreparedBlock) -> Result<&mut Self> {
245        if block.is_empty() {
246            return Ok(self);
247        }
248
249        let PreparedBlock {
250            compressed,
251            compressed_crc,
252            entries,
253            methods,
254            sizes,
255            crc,
256            sub_stream_sizes,
257            sub_stream_crcs,
258        } = block;
259
260        let compressed_len = compressed.len() as u64;
261        self.output
262            .write_all(&compressed)
263            .map_err(|e| Error::io_msg(e, "push_prepared_block: write".to_string()))?;
264
265        self.pack_info
266            .add_stream(compressed_len, Some(compressed_crc));
267        self.unpack_info.add_multiple(
268            methods,
269            sizes,
270            crc,
271            entries.len() as u64,
272            sub_stream_sizes,
273            sub_stream_crcs,
274        );
275        self.files.extend(entries);
276        Ok(self)
277    }
278
279    /// Copy a block of an existing archive into this one as it is encoded, without decoding it.
280    ///
281    /// The block's packed streams are copied from `source` byte for byte, and its coders, sizes
282    /// and CRCs are carried over as they were, so this costs a read and a write of the compressed
283    /// bytes and nothing else. Each of its entries is written under the name `rename` returns for
284    /// it, keeping the rest of its metadata. That is what makes renaming an entry, dropping one,
285    /// or adding to an archive cheap: write the new archive from the old one's blocks, raw.
286    ///
287    /// A block's entries go together, since leaving one out of a solid block means re-encoding
288    /// the others: to drop an entry, decode its block with [`BlockDecoder`](crate::BlockDecoder) and push the rest
289    /// again. Entries without data belong to no block, and are pushed with
290    /// [`push_archive_entry`](Self::push_archive_entry) and no reader.
291    ///
292    /// `archive` must be the one `source` holds, as [`Archive::read`] parsed it.
293    ///
294    /// # Errors
295    /// If `block_index` is out of range, the archive has no stream map for the block (it has no
296    /// `FilesInfo`), the block's offset in `source` overflows, or the block is missing a CRC
297    /// this writer would have to invent: it writes every sub-stream's CRC as defined.
298    pub fn push_raw_block<R: Read + Seek>(
299        &mut self,
300        archive: &Archive,
301        block_index: usize,
302        source: &mut R,
303        mut rename: impl FnMut(&ArchiveEntry) -> String,
304    ) -> Result<&mut Self> {
305        let block = archive
306            .blocks
307            .get(block_index)
308            .ok_or_else(|| Error::other(format!("push_raw_block: no block {block_index}")))?;
309        let map = &archive.stream_map;
310        // The stream map is only built along with the files, so an archive without a
311        // `FilesInfo` has blocks and no map of them.
312        let unmapped = || {
313            Error::other(format!(
314                "push_raw_block: block {block_index} is missing from the stream map"
315            ))
316        };
317
318        // Its sub-streams, which are its entries' sizes and CRCs.
319        let first_sub_stream = *map
320            .block_first_sub_stream_index
321            .get(block_index)
322            .ok_or_else(unmapped)?;
323        let sub_streams = first_sub_stream..first_sub_stream + block.num_unpack_sub_streams;
324        let info = archive.sub_streams_info.as_ref();
325        let mut sub_stream_sizes = Vec::with_capacity(sub_streams.len());
326        let mut sub_stream_crcs = Vec::with_capacity(sub_streams.len());
327        for index in sub_streams {
328            let crc = match info {
329                Some(info) if info.has_crc.contains(index) => info.crcs[index] as u32,
330                // A lone sub-stream may carry its CRC on the block instead.
331                _ if block.num_unpack_sub_streams == 1 && block.has_crc => block.crc as u32,
332                _ => {
333                    return Err(Error::other(format!(
334                        "push_raw_block: block {block_index} has an entry without a CRC"
335                    )));
336                }
337            };
338            sub_stream_crcs.push(crc);
339            sub_stream_sizes
340                .push(info.map_or(block.get_unpack_size(), |info| info.unpack_sizes[index]));
341        }
342
343        // Its entries, the files with data in it, in order. An empty entry between two of a
344        // solid block's may be mapped to it too, but it has no sub-stream there.
345        let entries: Vec<ArchiveEntry> = archive
346            .files
347            .iter()
348            .zip(&map.file_block_index)
349            .filter(|(entry, block)| entry.has_stream && **block == Some(block_index))
350            .map(|(entry, _)| ArchiveEntry {
351                name: rename(entry),
352                ..entry.clone()
353            })
354            .collect();
355        if entries.len() != block.num_unpack_sub_streams {
356            return Err(Error::other(format!(
357                "push_raw_block: block {block_index} has {} entries for {} sub-streams",
358                entries.len(),
359                block.num_unpack_sub_streams
360            )));
361        }
362
363        // Its packed streams, copied as they are.
364        let first_pack_stream = *map
365            .block_first_pack_stream_index
366            .get(block_index)
367            .ok_or_else(unmapped)?;
368        for pack_stream in first_pack_stream..first_pack_stream + block.packed_streams.len() {
369            let (Some(&size), Some(&stream_offset)) = (
370                archive.pack_sizes.get(pack_stream),
371                map.pack_stream_offsets.get(pack_stream),
372            ) else {
373                return Err(unmapped());
374            };
375            // The header's offsets are not bounded when it is read.
376            let offset = SIGNATURE_HEADER_SIZE
377                .checked_add(archive.pack_pos)
378                .and_then(|offset| offset.checked_add(stream_offset))
379                .ok_or_else(|| {
380                    Error::other(format!(
381                        "push_raw_block: block {block_index} lies past the end of any file"
382                    ))
383                })?;
384            source.seek(std::io::SeekFrom::Start(offset))?;
385            let copied = std::io::copy(&mut (&mut *source).take(size), &mut self.output)?;
386            if copied != size {
387                return Err(Error::other(format!(
388                    "push_raw_block: block {block_index} is truncated"
389                )));
390            }
391            let crc = archive
392                .pack_crcs_defined
393                .contains(pack_stream)
394                .then(|| archive.pack_crcs[pack_stream] as u32);
395            self.pack_info.add_stream(size, crc);
396        }
397
398        self.unpack_info.add_raw(
399            block,
400            block.num_unpack_sub_streams as u64,
401            sub_stream_sizes,
402            sub_stream_crcs,
403        );
404        self.files.extend(entries);
405        Ok(self)
406    }
407
408    /// Solid compression - packs `entries` into one pack.
409    ///
410    /// # Panics
411    /// * If `entries`'s length not equals to `reader.reader_len()`
412    pub fn push_archive_entries<R: Read>(
413        &mut self,
414        entries: Vec<ArchiveEntry>,
415        reader: Vec<SourceReader<R>>,
416    ) -> Result<&mut Self> {
417        let mut entries = entries;
418        let mut r = SeqReader::new(reader);
419        assert_eq!(r.reader_len(), entries.len());
420        let mut compressed_len = 0;
421        let mut compressed = CompressWrapWriter::new(&mut self.output, &mut compressed_len);
422        let content_methods = &self.content_methods;
423        let mut more_sizes: Vec<Rc<Cell<usize>>> = Vec::with_capacity(content_methods.len() - 1);
424
425        let (crc, size) = {
426            let mut w = Self::create_writer(content_methods, &mut compressed, &mut more_sizes)?;
427            let mut write_len = 0;
428            let mut w = CompressWrapWriter::new(&mut w, &mut write_len);
429            let mut buf = [0u8; 4096];
430
431            loop {
432                match r.read(&mut buf) {
433                    Ok(n) => {
434                        if n == 0 {
435                            break;
436                        }
437                        w.write_all(&buf[..n]).map_err(|e| {
438                            Error::io_msg(e, format!("Encode entries:{}", entries_names(&entries)))
439                        })?;
440                    }
441                    Err(e) => {
442                        return Err(Error::io_msg(
443                            e,
444                            format!("Encode entries:{}", entries_names(&entries)),
445                        ));
446                    }
447                }
448            }
449            w.flush().map_err(|e| {
450                let mut names = String::with_capacity(512);
451                for ele in entries.iter() {
452                    names.push_str(&ele.name);
453                    names.push(';');
454                    if names.len() > 512 {
455                        break;
456                    }
457                }
458                Error::io_msg(e, format!("Encode entry:{names}"))
459            })?;
460            w.write(&[]).map_err(|e| {
461                Error::io_msg(e, format!("Encode entry:{}", entries_names(&entries)))
462            })?;
463
464            (w.crc_value(), write_len)
465        };
466        let compressed_crc = compressed.crc_value();
467        let mut sub_stream_crcs = Vec::with_capacity(entries.len());
468        let mut sub_stream_sizes = Vec::with_capacity(entries.len());
469        for i in 0..entries.len() {
470            let entry = &mut entries[i];
471            let ri = &r[i];
472            entry.crc = ri.crc_value() as u64;
473            entry.size = ri.read_count() as u64;
474            sub_stream_crcs.push(entry.crc as u32);
475            sub_stream_sizes.push(entry.size);
476            entry.has_crc = true;
477        }
478
479        self.pack_info
480            .add_stream(compressed_len as u64, Some(compressed_crc));
481
482        let mut sizes = Vec::with_capacity(more_sizes.len() + 1);
483        sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
484        sizes.push(size as u64);
485
486        self.unpack_info.add_multiple(
487            content_methods.clone(),
488            sizes,
489            crc,
490            entries.len() as u64,
491            sub_stream_sizes,
492            sub_stream_crcs,
493        );
494
495        self.files.extend(entries);
496        Ok(self)
497    }
498
499    fn create_writer<'a, O: Write + 'a>(
500        methods: &[EncoderConfiguration],
501        out: O,
502        more_sized: &mut Vec<Rc<Cell<usize>>>,
503    ) -> Result<Box<dyn Write + 'a>> {
504        let mut encoder: Box<dyn Write> = Box::new(out);
505        let mut first = true;
506        for mc in methods.iter() {
507            if !first {
508                let counting = CountingWriter::new(encoder);
509                more_sized.push(counting.counting());
510                encoder = Box::new(encoder::add_encoder(counting, mc)?);
511            } else {
512                let counting = CountingWriter::new(encoder);
513                encoder = Box::new(encoder::add_encoder(counting, mc)?);
514            }
515            first = false;
516        }
517        Ok(encoder)
518    }
519
520    /// Finishes the compression.
521    pub fn finish(mut self) -> std::io::Result<W> {
522        let mut header: Vec<u8> = Vec::with_capacity(64 * 1024);
523        self.write_encoded_header(&mut header)?;
524        let header_pos = self.output.stream_position()?;
525        self.output.write_all(&header)?;
526        let crc32 = crc32fast::hash(&header);
527        let mut hh = [0u8; SIGNATURE_HEADER_SIZE as usize];
528        {
529            let mut hhw = hh.as_mut_slice();
530            //sig
531            hhw.write_all(SEVEN_Z_SIGNATURE)?;
532            //version
533            hhw.write_u8(0)?;
534            hhw.write_u8(4)?;
535            //placeholder for crc: index = 8
536            hhw.write_u32(0)?;
537
538            // start header
539            hhw.write_u64(header_pos - SIGNATURE_HEADER_SIZE)?;
540            hhw.write_u64(0xFFFFFFFF & header.len() as u64)?;
541            hhw.write_u32(crc32)?;
542        }
543        let crc32 = crc32fast::hash(&hh[12..]);
544        hh[8..12].copy_from_slice(&crc32.to_le_bytes());
545
546        self.output.seek(std::io::SeekFrom::Start(0))?;
547        self.output.write_all(&hh)?;
548        self.output.flush()?;
549        Ok(self.output)
550    }
551
552    fn write_header<H: Write>(&mut self, header: &mut H) -> std::io::Result<()> {
553        header.write_u8(K_HEADER)?;
554        header.write_u8(K_MAIN_STREAMS_INFO)?;
555        self.write_streams_info(header)?;
556        self.write_files_info(header)?;
557        header.write_u8(K_END)?;
558        Ok(())
559    }
560
561    fn write_encoded_header<H: Write>(&mut self, header: &mut H) -> std::io::Result<()> {
562        let mut raw_header = Vec::with_capacity(64 * 1024);
563        self.write_header(&mut raw_header)?;
564        let mut pack_info = PackInfo::default();
565
566        let position = self.output.stream_position()?;
567        let pos = position - SIGNATURE_HEADER_SIZE;
568        pack_info.pos = pos;
569
570        let mut more_sizes = vec![];
571        let size = raw_header.len() as u64;
572        let crc32 = crc32fast::hash(&raw_header);
573        let mut methods = vec![];
574
575        let mut must_encrypt_header = false;
576
577        if self.encrypt_header {
578            for conf in self.content_methods.iter() {
579                if conf.method.id() == EncoderMethod::AES256_SHA256.id() {
580                    methods.push(conf.clone());
581                    must_encrypt_header = true;
582                    break;
583                }
584            }
585        }
586
587        methods.push(EncoderConfiguration::new(EncoderMethod::LZMA));
588
589        let methods = Arc::new(methods);
590
591        let mut encoded_data = Vec::with_capacity(size as usize / 2);
592
593        let mut compress_size = 0;
594        let mut compressed = CompressWrapWriter::new(&mut encoded_data, &mut compress_size);
595        {
596            let mut encoder = Self::create_writer(&methods, &mut compressed, &mut more_sizes)
597                .map_err(std::io::Error::other)?;
598            encoder.write_all(&raw_header)?;
599            encoder.flush()?;
600            let _ = encoder.write(&[])?;
601        }
602
603        let compress_crc = compressed.crc_value();
604        let compress_size = *compressed.bytes_written;
605
606        if !must_encrypt_header && compress_size as u64 + 20 >= size {
607            // We have an unencrypted header and the compression made increased the data size,
608            // so we write the raw header data without compressing it to save space.
609            header.write_all(&raw_header)?;
610            return Ok(());
611        }
612        self.output.write_all(&encoded_data[..compress_size])?;
613
614        pack_info.add_stream(compress_size as u64, Some(compress_crc));
615
616        let mut unpack_info = UnpackInfo::default();
617        let mut sizes = Vec::with_capacity(1 + more_sizes.len());
618        sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
619        sizes.push(size);
620        unpack_info.add(methods, sizes, crc32);
621
622        header.write_u8(K_ENCODED_HEADER)?;
623
624        pack_info.write_to(header)?;
625        unpack_info.write_to(header)?;
626        unpack_info.write_substreams(header)?;
627
628        header.write_u8(K_END)?;
629
630        Ok(())
631    }
632
633    fn write_streams_info<H: Write>(&mut self, header: &mut H) -> std::io::Result<()> {
634        if self.pack_info.len() > 0 {
635            self.pack_info.write_to(header)?;
636            self.unpack_info.write_to(header)?;
637        }
638        self.unpack_info.write_substreams(header)?;
639
640        header.write_u8(K_END)?;
641        Ok(())
642    }
643
644    fn write_files_info<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
645        header.write_u8(K_FILES_INFO)?;
646        write_u64(header, self.files.len() as u64)?;
647        self.write_file_empty_streams(header)?;
648        self.write_file_empty_files(header)?;
649        self.write_file_anti_items(header)?;
650        self.write_file_names(header)?;
651        self.write_file_ctimes(header)?;
652        self.write_file_atimes(header)?;
653        self.write_file_mtimes(header)?;
654        self.write_file_windows_attrs(header)?;
655        header.write_u8(K_END)?;
656        Ok(())
657    }
658
659    fn write_file_empty_streams<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
660        let mut has_empty = false;
661        for entry in self.files.iter() {
662            if !entry.has_stream {
663                has_empty = true;
664                break;
665            }
666        }
667        if has_empty {
668            header.write_u8(K_EMPTY_STREAM)?;
669            let mut bitset = BitSet::with_capacity(self.files.len());
670            for (i, entry) in self.files.iter().enumerate() {
671                if !entry.has_stream {
672                    bitset.insert(i);
673                }
674            }
675            let mut temp: Vec<u8> = Vec::with_capacity(bitset.len() / 8 + 1);
676            write_bit_set(&mut temp, &bitset)?;
677            write_u64(header, temp.len() as u64)?;
678            header.write_all(temp.as_slice())?;
679        }
680        Ok(())
681    }
682
683    fn write_file_empty_files<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
684        let mut has_empty = false;
685        let mut empty_stream_counter = 0;
686        let mut bitset = BitSet::new();
687        for entry in self.files.iter() {
688            if !entry.has_stream {
689                let is_dir = entry.is_directory();
690                has_empty |= !is_dir;
691                if !is_dir {
692                    bitset.insert(empty_stream_counter);
693                }
694                empty_stream_counter += 1;
695            }
696        }
697        if has_empty {
698            header.write_u8(K_EMPTY_FILE)?;
699
700            let mut temp: Vec<u8> = Vec::with_capacity(bitset.len() / 8 + 1);
701            write_bit_set(&mut temp, &bitset)?;
702            write_u64(header, temp.len() as u64)?;
703            header.write_all(&temp)?;
704        }
705        Ok(())
706    }
707
708    fn write_file_anti_items<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
709        let mut has_anti = false;
710        let mut counter = 0;
711        let mut bitset = BitSet::new();
712        for entry in self.files.iter() {
713            if !entry.has_stream {
714                let is_anti = entry.is_anti_item();
715                has_anti |= is_anti;
716                if is_anti {
717                    bitset.insert(counter);
718                }
719                counter += 1;
720            }
721        }
722        if has_anti {
723            header.write_u8(K_ANTI)?;
724
725            let mut temp: Vec<u8> = Vec::with_capacity(bitset.len() / 8 + 1);
726            write_bit_set(&mut temp, &bitset)?;
727            write_u64(header, temp.len() as u64)?;
728            header.write_all(temp.as_slice())?;
729        }
730        Ok(())
731    }
732
733    fn write_file_names<H: Write>(&self, header: &mut H) -> std::io::Result<()> {
734        header.write_u8(K_NAME)?;
735        let mut temp: Vec<u8> = Vec::with_capacity(128);
736        let out = &mut temp;
737        out.write_u8(0)?;
738        for file in self.files.iter() {
739            for c in file.name().encode_utf16() {
740                let buf = c.to_le_bytes();
741                out.write_all(&buf)?;
742            }
743            out.write_all(&[0u8; 2])?;
744        }
745        write_u64(header, temp.len() as u64)?;
746        header.write_all(temp.as_slice())?;
747        Ok(())
748    }
749
750    write_times!(
751        write_file_ctimes,
752        K_C_TIME,
753        has_creation_date,
754        creation_date
755    );
756    write_times!(write_file_atimes, K_A_TIME, has_access_date, access_date);
757    write_times!(
758        write_file_mtimes,
759        K_M_TIME,
760        has_last_modified_date,
761        last_modified_date
762    );
763    write_times!(
764        write_file_windows_attrs,
765        K_WIN_ATTRIBUTES,
766        has_windows_attributes,
767        windows_attributes,
768        write_u32
769    );
770}
771
772impl<W: Write + Seek> AutoFinish for ArchiveWriter<W> {
773    fn finish_ignore_error(self) {
774        let _ = self.finish();
775    }
776}
777
778pub(crate) fn write_u64<W: Write>(header: &mut W, mut value: u64) -> std::io::Result<()> {
779    let mut first = 0;
780    let mut mask = 0x80;
781    let mut i = 0;
782    while i < 8 {
783        if value < (1u64 << (7 * (i + 1))) {
784            first |= value >> (8 * i);
785            break;
786        }
787        first |= mask;
788        mask >>= 1;
789        i += 1;
790    }
791    header.write_u8((first & 0xFF) as u8)?;
792    while i > 0 {
793        header.write_u8((value & 0xFF) as u8)?;
794        value >>= 8;
795        i -= 1;
796    }
797    Ok(())
798}
799
800struct CompressWrapWriter<'a, W> {
801    writer: W,
802    crc: Hasher,
803    cache: Vec<u8>,
804    bytes_written: &'a mut usize,
805}
806
807impl<'a, W: Write> CompressWrapWriter<'a, W> {
808    pub fn new(writer: W, bytes_written: &'a mut usize) -> Self {
809        Self {
810            writer,
811            crc: Hasher::new(),
812            cache: Vec::with_capacity(8192),
813            bytes_written,
814        }
815    }
816
817    pub fn crc_value(&mut self) -> u32 {
818        let crc = std::mem::replace(&mut self.crc, Hasher::new());
819        crc.finalize()
820    }
821}
822
823impl<W: Write> Write for CompressWrapWriter<'_, W> {
824    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
825        self.cache.resize(buf.len(), Default::default());
826        let len = self.writer.write(buf)?;
827        self.crc.update(&buf[..len]);
828        *self.bytes_written += len;
829        Ok(len)
830    }
831
832    fn flush(&mut self) -> std::io::Result<()> {
833        self.writer.flush()
834    }
835}
836
837/// A solid block compressed away from any writer, ready to be appended by
838/// [`ArchiveWriter::push_prepared_block`].
839///
840/// **This holds the entire compressed block in memory** until it is pushed, where
841/// [`ArchiveWriter::push_archive_entries`] streams into the output as it encodes. That is inherent
842/// to preparing a block off-thread, and it is what a caller sizing its batches is signing up for:
843/// peak memory is roughly the compressed size of every block in flight at once.
844#[derive(Debug)]
845pub struct PreparedBlock {
846    compressed: Vec<u8>,
847    compressed_crc: u32,
848    entries: Vec<ArchiveEntry>,
849    methods: Arc<Vec<EncoderConfiguration>>,
850    sizes: Vec<u64>,
851    crc: u32,
852    sub_stream_sizes: Vec<u64>,
853    sub_stream_crcs: Vec<u32>,
854}
855
856impl PreparedBlock {
857    /// Compressed size in bytes, before it is appended.
858    pub fn compressed_len(&self) -> usize {
859        self.compressed.len()
860    }
861
862    /// Number of entries in the block.
863    pub fn len(&self) -> usize {
864        self.entries.len()
865    }
866
867    /// Whether the block holds no entries.
868    pub fn is_empty(&self) -> bool {
869        self.entries.is_empty()
870    }
871}
872
873/// Compress `entries` into one solid block using `methods`, without a writer.
874///
875/// Mirrors the encoding half of [`ArchiveWriter::push_archive_entries`], writing into memory instead
876/// of the archive. Safe to call on a worker thread; the result is appended later with
877/// [`ArchiveWriter::push_prepared_block`], which is what fixes the order.
878///
879/// Returns an error if `methods` is empty, or if `entries` and `reader` differ in length.
880pub fn prepare_block<R: Read>(
881    methods: Arc<Vec<EncoderConfiguration>>,
882    entries: Vec<ArchiveEntry>,
883    reader: Vec<SourceReader<R>>,
884) -> Result<PreparedBlock> {
885    if methods.is_empty() {
886        return Err(Error::other("prepare_block: `methods` must not be empty"));
887    }
888
889    let mut entries = entries;
890    let mut r = SeqReader::new(reader);
891    if r.reader_len() != entries.len() {
892        return Err(Error::other(format!(
893            "prepare_block: {} entries against {} readers",
894            entries.len(),
895            r.reader_len()
896        )));
897    }
898
899    let mut out: Vec<u8> = Vec::new();
900    let mut more_sizes: Vec<Rc<Cell<usize>>> = Vec::with_capacity(methods.len() - 1);
901
902    let (crc, size, compressed_crc) = {
903        // Outer wrapper: the CRC of the compressed bytes, computed as they are produced on this
904        // thread. `push_prepared_block` would otherwise have to make a second pass over the whole
905        // buffer on the serialized side, which is the work this function exists to move off it.
906        let mut compressed_len = 0;
907        let mut compressed = CompressWrapWriter::new(&mut out, &mut compressed_len);
908        let (crc, size) = {
909            let mut w = ArchiveWriter::<std::io::Cursor<Vec<u8>>>::create_writer(
910                &methods,
911                &mut compressed,
912                &mut more_sizes,
913            )?;
914            let mut write_len = 0;
915            let mut w = CompressWrapWriter::new(&mut w, &mut write_len);
916            let mut buf = [0u8; 4096];
917            loop {
918                let n = r.read(&mut buf).map_err(|e| {
919                    Error::io_msg(
920                        e,
921                        format!("prepare_block: read source:{}", entries_names(&entries)),
922                    )
923                })?;
924                if n == 0 {
925                    break;
926                }
927                w.write_all(&buf[..n]).map_err(|e| {
928                    Error::io_msg(
929                        e,
930                        format!("prepare_block: encode:{}", entries_names(&entries)),
931                    )
932                })?;
933            }
934            w.flush().map_err(|e| {
935                Error::io_msg(
936                    e,
937                    format!("prepare_block: flush:{}", entries_names(&entries)),
938                )
939            })?;
940            w.write(&[]).map_err(|e| {
941                Error::io_msg(
942                    e,
943                    format!("prepare_block: finish:{}", entries_names(&entries)),
944                )
945            })?;
946            (w.crc_value(), write_len)
947        };
948        (crc, size, compressed.crc_value())
949    };
950
951    let mut sub_stream_crcs = Vec::with_capacity(entries.len());
952    let mut sub_stream_sizes = Vec::with_capacity(entries.len());
953    for i in 0..entries.len() {
954        let entry = &mut entries[i];
955        let ri = &r[i];
956        entry.crc = ri.crc_value() as u64;
957        entry.size = ri.read_count() as u64;
958        sub_stream_crcs.push(entry.crc as u32);
959        sub_stream_sizes.push(entry.size);
960        entry.has_crc = true;
961    }
962
963    let mut sizes = Vec::with_capacity(more_sizes.len() + 1);
964    sizes.extend(more_sizes.iter().map(|s| s.get() as u64));
965    sizes.push(size as u64);
966
967    Ok(PreparedBlock {
968        compressed: out,
969        compressed_crc,
970        entries,
971        methods,
972        sizes,
973        crc,
974        sub_stream_sizes,
975        sub_stream_crcs,
976    })
977}