1use crc32fast::Hasher;
2use std::io::{self, Seek, SeekFrom, Write};
3
4use crate::bindle::Bindle;
5use crate::entry::Entry;
6
7pub struct Writer<'a> {
27 pub(crate) bindle: &'a mut Bindle,
28 pub(crate) encoder: Option<zstd::Encoder<'a, std::fs::File>>,
29 pub(crate) name: String,
30 pub(crate) start_offset: u64,
31 pub(crate) uncompressed_size: u64,
32 pub(crate) crc32_hasher: Hasher,
33}
34
35impl<'a> Drop for Writer<'a> {
36 fn drop(&mut self) {
37 let _ = self.close_drop();
38 }
39}
40
41impl<'a> std::io::Write for Writer<'a> {
42 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
43 self.write_chunk(buf)?;
44 Ok(buf.len())
45 }
46
47 fn flush(&mut self) -> io::Result<()> {
48 Ok(())
49 }
50}
51
52impl<'a> Writer<'a> {
53 pub fn write_chunk(&mut self, data: &[u8]) -> io::Result<()> {
54 if self.name.is_empty() {
55 return Err(std::io::Error::new(std::io::ErrorKind::Other, "closed"));
56 }
57
58 self.uncompressed_size += data.len() as u64;
59 self.crc32_hasher.update(data);
60
61 match &mut self.encoder {
62 Some(encoder) => {
63 encoder.write_all(data)?;
65 }
66 None => {
67 self.bindle.file.write_all(data)?;
69 }
70 }
71
72 Ok(())
73 }
74
75 fn close_drop(&mut self) -> io::Result<()> {
76 if self.name.is_empty() {
77 return Ok(());
78 }
79
80 let (compression_type, current_pos) = match self.encoder.take() {
81 Some(encoder) => {
82 let mut f = encoder.finish()?;
84 let pos = f.stream_position()?;
85 self.bindle.file.seek(SeekFrom::Start(pos))?;
86 (1, pos)
87 }
88 None => {
89 let pos = self.bindle.file.stream_position()?;
91 (0, pos)
92 }
93 };
94
95 let compressed_size = current_pos - self.start_offset;
96
97 let pad_len = crate::pad::<8, u64>(current_pos);
99 if pad_len > 0 {
100 crate::write_padding(&mut self.bindle.file, pad_len as usize)?;
101 }
102
103 self.bindle.data_end = current_pos + pad_len;
104
105 let crc32_value = self.crc32_hasher.clone().finalize();
106
107 let mut entry = Entry::default();
108 entry.set_offset(self.start_offset);
109 entry.set_compressed_size(compressed_size);
110 entry.set_uncompressed_size(self.uncompressed_size);
111 entry.set_crc32(crc32_value);
112 entry.set_name_len(self.name.len() as u16);
113 entry.compression_type = compression_type;
114
115 self.bindle.index.insert(self.name.clone(), entry);
116 self.name.clear(); self.bindle.file.lock_shared()?;
120 Ok(())
121 }
122
123 pub fn close(mut self) -> io::Result<()> {
127 self.close_drop()
128 }
129}