keramics_compression/
adc.rs1use keramics_core::ErrorTrace;
19use keramics_core::mediator::{Mediator, MediatorReference};
20use keramics_types::bytes_to_u16_be;
21
22pub struct AdcContext {
24 mediator: MediatorReference,
26
27 pub uncompressed_data_size: usize,
29}
30
31impl AdcContext {
32 pub fn new() -> Self {
34 Self {
35 mediator: Mediator::current(),
36 uncompressed_data_size: 0,
37 }
38 }
39
40 pub fn decompress(
42 &mut self,
43 compressed_data: &[u8],
44 uncompressed_data: &mut [u8],
45 ) -> Result<(), ErrorTrace> {
46 let mut compressed_data_offset: usize = 0;
47 let compressed_data_size: usize = compressed_data.len();
48
49 let mut uncompressed_data_offset: usize = 0;
50 let uncompressed_data_size: usize = uncompressed_data.len();
51
52 if self.mediator.debug_output {
53 self.mediator.debug_print(format!("AdcContext {{\n",));
54 }
55 while compressed_data_offset < compressed_data_size {
56 if uncompressed_data_offset >= uncompressed_data_size {
57 break;
58 }
59 if compressed_data_offset >= compressed_data_size {
60 return Err(keramics_core::error_trace_new!(
61 "Invalid compressed data value too small"
62 ));
63 }
64 let oppcode: u8 = compressed_data[compressed_data_offset];
65 compressed_data_offset += 1;
66
67 if self.mediator.debug_output {
68 self.mediator
69 .debug_print(format!(" oppcode: {}\n", oppcode));
70 }
71 if (oppcode & 0x80) != 0 {
72 let literal_size: u8 = (oppcode & 0x7f) + 1;
73
74 if literal_size as usize > compressed_data_size - compressed_data_offset {
75 return Err(keramics_core::error_trace_new!(
76 "Literal size value exceeds compressed data size"
77 ));
78 }
79 if literal_size as usize > uncompressed_data_size - uncompressed_data_offset {
80 return Err(keramics_core::error_trace_new!(
81 "Literal size value exceeds uncompressed data size"
82 ));
83 }
84 let compressed_data_end_offset: usize =
85 compressed_data_offset + literal_size as usize;
86 let uncompressed_data_end_offset: usize =
87 uncompressed_data_offset + literal_size as usize;
88
89 if self.mediator.debug_output {
90 self.mediator.debug_print(format!(" literal data:\n"));
91 self.mediator.debug_print_data(
92 &compressed_data[compressed_data_offset..compressed_data_end_offset],
93 true,
94 );
95 }
96 uncompressed_data[uncompressed_data_offset..uncompressed_data_end_offset]
97 .copy_from_slice(
98 &compressed_data[compressed_data_offset..compressed_data_end_offset],
99 );
100
101 compressed_data_offset = compressed_data_end_offset;
102 uncompressed_data_offset = uncompressed_data_end_offset;
103 } else {
104 let match_size: u8;
105 let distance: u16;
106
107 if (oppcode & 0x40) != 0 {
108 if 2 > compressed_data_size - compressed_data_offset {
109 return Err(keramics_core::error_trace_new!(
110 "Invalid compressed data value too small"
111 ));
112 }
113 match_size = (oppcode & 0x3f) + 4;
114 distance = bytes_to_u16_be!(compressed_data, compressed_data_offset);
115
116 compressed_data_offset += 2;
117 } else {
118 if compressed_data_offset >= compressed_data_size {
119 return Err(keramics_core::error_trace_new!(
120 "Invalid compressed data value too small"
121 ));
122 }
123 match_size = ((oppcode & 0x3f) >> 2) + 3;
124 distance = ((oppcode as u16 & 0x03) << 8)
125 | compressed_data[compressed_data_offset] as u16;
126
127 compressed_data_offset += 1;
128 }
129 if uncompressed_data_offset < 1 {
130 return Err(keramics_core::error_trace_new!(
131 "Invalid uncompressed data offset value out of bounds"
132 ));
133 }
134 if distance as usize >= uncompressed_data_offset {
135 return Err(keramics_core::error_trace_new!(
136 "Invalid distance value exceeds uncompressed data offset"
137 ));
138 }
139 if match_size as usize > uncompressed_data_size - uncompressed_data_offset {
140 return Err(keramics_core::error_trace_new!(
141 "Invalid match size value exceeds uncompressed data size"
142 ));
143 }
144 let match_offset: usize = uncompressed_data_offset - distance as usize - 1;
145 let mut match_end_offset: usize = match_offset;
146
147 for _ in 0..match_size {
148 uncompressed_data[uncompressed_data_offset] =
149 uncompressed_data[match_end_offset];
150
151 match_end_offset += 1;
152 uncompressed_data_offset += 1;
153 }
154 if self.mediator.debug_output {
155 self.mediator
156 .debug_print(format!(" match offset: {}\n", match_offset));
157 self.mediator.debug_print(format!(" match data:\n"));
158 self.mediator
159 .debug_print_data(&uncompressed_data[match_offset..match_end_offset], true);
160 }
161 }
162 }
163 if self.mediator.debug_output {
164 self.mediator.debug_print(format!("}}\n\n",));
165 }
166 self.uncompressed_data_size = uncompressed_data_offset;
167
168 Ok(())
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_decompress() -> Result<(), ErrorTrace> {
178 let test_data: [u8; 10] = [0x83, 0xfe, 0xed, 0xfa, 0xce, 0x00, 0x00, 0x40, 0x00, 0x06];
179 let mut test_context: AdcContext = AdcContext::new();
180
181 let mut uncompressed_data: Vec<u8> = vec![0; 11];
182 test_context.decompress(&test_data, &mut uncompressed_data)?;
183 assert_eq!(test_context.uncompressed_data_size, 11);
184
185 let expected_data: [u8; 11] = [
186 0xfe, 0xed, 0xfa, 0xce, 0xce, 0xce, 0xce, 0xfe, 0xed, 0xfa, 0xce,
187 ];
188 assert_eq!(uncompressed_data, expected_data);
189
190 Ok(())
191 }
192}