1use alloc::vec;
3use alloc::vec::Vec;
4
5use crate::decompress;
6#[cfg(feature = "encoder")]
7use crate::{CompressionMode, compress_with_mode};
8use crate::{Error, Result};
9
10const MAGIC_0: u8 = b'Z';
11const MAGIC_1: u8 = b'V';
12const TYPE_UNCOMPRESSED: u8 = 0;
13const TYPE_COMPRESSED: u8 = 1;
14const TYPE0_HDR_SIZE: usize = 5;
15const TYPE1_HDR_SIZE: usize = 7;
16
17#[cfg(feature = "encoder")]
24pub fn encode_blocks(input: &[u8], block_size: usize) -> Result<Vec<u8>> {
25 encode_blocks_with_mode(input, block_size, CompressionMode::Normal)
26}
27
28#[cfg(feature = "encoder")]
36pub fn encode_blocks_with_mode(
37 input: &[u8],
38 block_size: usize,
39 mode: CompressionMode,
40) -> Result<Vec<u8>> {
41 if block_size == 0 || block_size > usize::from(u16::MAX) {
42 return Err(Error::InvalidParameter);
43 }
44
45 let mut output = Vec::new();
46
47 for block in input.chunks(block_size) {
48 let max_try = block.len().saturating_sub(4);
49 let mut compressed = vec![0u8; max_try];
50
51 let encoded_len = if max_try == 0 {
52 Err(Error::OutputTooSmall)
53 } else {
54 compress_with_mode(block, &mut compressed, mode)
55 };
56
57 match encoded_len {
58 Ok(cs) => {
59 let cs_u16 = u16::try_from(cs).map_err(|_| Error::InvalidParameter)?;
60 let us_u16 = u16::try_from(block.len()).map_err(|_| Error::InvalidParameter)?;
61
62 output.push(MAGIC_0);
63 output.push(MAGIC_1);
64 output.push(TYPE_COMPRESSED);
65 output.extend_from_slice(&cs_u16.to_be_bytes());
66 output.extend_from_slice(&us_u16.to_be_bytes());
67 output.extend_from_slice(&compressed[..cs]);
68 }
69 Err(Error::OutputTooSmall) => {
70 let us_u16 = u16::try_from(block.len()).map_err(|_| Error::InvalidParameter)?;
71
72 output.push(MAGIC_0);
73 output.push(MAGIC_1);
74 output.push(TYPE_UNCOMPRESSED);
75 output.extend_from_slice(&us_u16.to_be_bytes());
76 output.extend_from_slice(block);
77 }
78 Err(err) => return Err(err),
79 }
80 }
81
82 Ok(output)
83}
84
85pub fn decode_blocks(input: &[u8]) -> Result<Vec<u8>> {
101 let mut ip = 0usize;
102 let mut output = Vec::new();
103
104 while ip < input.len() {
105 if input[ip] == 0 {
106 break;
107 }
108
109 if input.len() - ip < TYPE0_HDR_SIZE {
110 return Err(Error::InvalidHeader);
111 }
112 if input[ip] != MAGIC_0 || input[ip + 1] != MAGIC_1 {
113 return Err(Error::InvalidHeader);
114 }
115
116 let block_type = input[ip + 2];
117 match block_type {
118 TYPE_UNCOMPRESSED => {
119 let uncompressed_len =
120 usize::from(u16::from_be_bytes([input[ip + 3], input[ip + 4]]));
121 ip += TYPE0_HDR_SIZE;
122 if input.len() - ip < uncompressed_len {
123 return Err(Error::InvalidData);
124 }
125 output.extend_from_slice(&input[ip..ip + uncompressed_len]);
126 ip += uncompressed_len;
127 }
128 TYPE_COMPRESSED => {
129 if input.len() - ip < TYPE1_HDR_SIZE {
130 return Err(Error::InvalidHeader);
131 }
132 let compressed_len =
133 usize::from(u16::from_be_bytes([input[ip + 3], input[ip + 4]]));
134 let uncompressed_len =
135 usize::from(u16::from_be_bytes([input[ip + 5], input[ip + 6]]));
136 ip += TYPE1_HDR_SIZE;
137
138 if input.len() - ip < compressed_len {
139 return Err(Error::InvalidData);
140 }
141
142 let mut block = vec![0u8; uncompressed_len];
143 let written = decompress(&input[ip..ip + compressed_len], &mut block)?;
144 if written != uncompressed_len {
145 return Err(Error::InvalidData);
146 }
147 output.extend_from_slice(&block);
148 ip += compressed_len;
149 }
150 other => return Err(Error::UnknownBlockType(other)),
151 }
152 }
153
154 Ok(output)
155}