ryg_rans_rs_cli/container/
writer.rs1use crate::container::MAGIC;
4use crate::container::block::Block;
5use crate::container::footer::FileFooter;
6use crate::container::header::FileHeader;
7use crate::error::AppError;
8use sha2::{Digest, Sha256};
9use std::io::Write;
10
11pub struct ContainerWriter<W: Write> {
13 writer: W,
14 header_written: bool,
15 footer_written: bool,
16 block_count: u64,
17 total_uncompressed: u64,
18 total_payload: u64,
19 hasher: Sha256,
20 decoded_hasher: Sha256,
21}
22
23impl<W: Write> ContainerWriter<W> {
24 pub fn new(writer: W) -> Self {
26 Self {
27 writer,
28 header_written: false,
29 footer_written: false,
30 block_count: 0,
31 total_uncompressed: 0,
32 total_payload: 0,
33 hasher: Sha256::new(),
34 decoded_hasher: Sha256::new(),
35 }
36 }
37
38 pub fn write_header(&mut self, header: &FileHeader) -> Result<(), AppError> {
40 let bytes = header.to_bytes();
41 self.writer.write_all(&bytes).map_err(|e| {
42 AppError::Io(crate::error::IoError {
43 path: None,
44 detail: format!("write header: {}", e),
45 })
46 })?;
47 self.hasher.update(&bytes);
48 self.header_written = true;
49 Ok(())
50 }
51
52 pub fn write_block(&mut self, block: &Block, decoded_data: &[u8]) -> Result<(), AppError> {
54 let bytes = block.to_bytes();
55 self.writer.write_all(&bytes).map_err(|e| {
56 AppError::Io(crate::error::IoError {
57 path: None,
58 detail: format!("write block {}: {}", block.block_index, e),
59 })
60 })?;
61
62 self.hasher.update(&bytes);
63 self.decoded_hasher.update(decoded_data);
64
65 self.block_count += 1;
66 self.total_uncompressed = self
67 .total_uncompressed
68 .checked_add(block.uncompressed_length as u64)
69 .unwrap();
70 self.total_payload = self
71 .total_payload
72 .checked_add(block.payload.len() as u64)
73 .unwrap();
74 Ok(())
75 }
76
77 pub fn write_footer(&mut self) -> Result<FileFooter, AppError> {
79 use sha2::Digest;
80 let hasher_clone = std::mem::replace(&mut self.hasher, Sha256::new());
81 let container_hash: [u8; 32] = hasher_clone.finalize().into();
82 let dec_hasher_clone = std::mem::replace(&mut self.decoded_hasher, Sha256::new());
83 let decoded_hash: [u8; 32] = dec_hasher_clone.finalize().into();
84
85 let footer = FileFooter::compute(
86 self.block_count,
87 self.total_uncompressed,
88 self.total_payload,
89 container_hash,
90 decoded_hash,
91 );
92
93 let bytes = footer.to_bytes();
94 self.writer.write_all(&bytes).map_err(|e| {
95 AppError::Io(crate::error::IoError {
96 path: None,
97 detail: format!("write footer: {}", e),
98 })
99 })?;
100
101 self.footer_written = true;
102 Ok(footer)
103 }
104
105 pub fn flush(&mut self) -> Result<(), AppError> {
107 self.writer.flush().map_err(|e| {
108 AppError::Io(crate::error::IoError {
109 path: None,
110 detail: format!("flush: {}", e),
111 })
112 })
113 }
114
115 pub fn into_inner(self) -> W {
117 self.writer
118 }
119}