/src/rust-brotli/src/concat/mod.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use core::cmp::min; |
2 | | |
3 | | #[repr(C)] |
4 | | #[derive(Debug, Clone, Copy, PartialEq)] |
5 | | pub enum BroCatliResult { |
6 | | Success = 0, |
7 | | NeedsMoreInput = 1, |
8 | | NeedsMoreOutput = 2, |
9 | | BrotliFileNotCraftedForAppend = 124, |
10 | | InvalidWindowSize = 125, |
11 | | WindowSizeLargerThanPreviousFile = 126, |
12 | | BrotliFileNotCraftedForConcatenation = 127, |
13 | | } |
14 | | |
15 | | const NUM_STREAM_HEADER_BYTES: usize = 5; |
16 | | |
17 | | #[derive(Clone, Copy)] |
18 | | struct NewStreamData { |
19 | | bytes_so_far: [u8; NUM_STREAM_HEADER_BYTES], |
20 | | num_bytes_read: u8, |
21 | | num_bytes_written: Option<u8>, |
22 | | } |
23 | | impl NewStreamData { |
24 | | pub fn new() -> NewStreamData { |
25 | | NewStreamData { |
26 | | bytes_so_far: [0, 0, 0, 0, 0], |
27 | | num_bytes_read: 0, |
28 | | num_bytes_written: None, |
29 | | } |
30 | | } |
31 | | fn sufficient(&self) -> bool { |
32 | | if self.num_bytes_read == 4 && (127 & self.bytes_so_far[0]) != 17 { |
33 | | return true; |
34 | | } |
35 | | self.num_bytes_read == 5 |
36 | | } |
37 | | } |
38 | | |
39 | | fn parse_window_size(bytes_so_far: &[u8]) -> Result<(u8, usize), ()> { |
40 | | // returns window_size and offset in stream in bits |
41 | | if bytes_so_far[0] & 1 == 0 { |
42 | | return Ok((16, 1)); |
43 | | } |
44 | | match bytes_so_far[0] & 15 { |
45 | | 0x3 => return Ok((18, 4)), |
46 | | 0x5 => return Ok((19, 4)), |
47 | | 0x7 => return Ok((20, 4)), |
48 | | 0x9 => return Ok((21, 4)), |
49 | | 0xb => return Ok((22, 4)), |
50 | | 0xd => return Ok((23, 4)), |
51 | | 0xf => return Ok((24, 4)), |
52 | | _ => match bytes_so_far[0] & 127 { |
53 | | 0x71 => return Ok((15, 7)), |
54 | | 0x61 => return Ok((14, 7)), |
55 | | 0x51 => return Ok((13, 7)), |
56 | | 0x41 => return Ok((12, 7)), |
57 | | 0x31 => return Ok((11, 7)), |
58 | | 0x21 => return Ok((10, 7)), |
59 | | 0x1 => return Ok((17, 7)), |
60 | | _ => {} |
61 | | }, |
62 | | } |
63 | | if (bytes_so_far[0] & 0x80) != 0 { |
64 | | return Err(()); |
65 | | } |
66 | | let ret = bytes_so_far[1] & 0x3f; |
67 | | if !(10..=30).contains(&ret) { |
68 | | return Err(()); |
69 | | } |
70 | | Ok((ret, 14)) |
71 | | } |
72 | | |
73 | | fn detect_varlen_offset(bytes_so_far: &[u8]) -> Result<(usize), ()> { |
74 | | // returns offfset in bits |
75 | | let (_, mut offset) = match parse_window_size(bytes_so_far) { |
76 | | Ok(x) => x, |
77 | | Err(_) => return Err(()), |
78 | | }; |
79 | | let mut bytes = 0u64; |
80 | | for (index, item) in bytes_so_far.iter().enumerate() { |
81 | | bytes |= u64::from(*item) << (index * 8); |
82 | | } |
83 | | bytes >>= offset; |
84 | | offset += 1; |
85 | | if (bytes & 1) != 0 { |
86 | | // ISLAST |
87 | | bytes >>= 1; |
88 | | offset += 1; |
89 | | if (bytes & 1) != 0 { |
90 | | // ISLASTEMPTY |
91 | | return Ok(offset); |
92 | | } |
93 | | } |
94 | | bytes >>= 1; |
95 | | let mut mnibbles = bytes & 3; |
96 | | bytes >>= 2; |
97 | | offset += 2; |
98 | | if mnibbles == 3 { |
99 | | // metadata block |
100 | | if (bytes & 1) != 0 { |
101 | | return Err(()); // reserved, must be zero |
102 | | } |
103 | | bytes >>= 1; |
104 | | offset += 1; |
105 | | let mskipbytes = bytes & ((1 << 2) - 1); |
106 | | offset += 2; |
107 | | offset += (mskipbytes as usize) * 8; // next item is byte aligned |
108 | | return Ok(offset); |
109 | | } |
110 | | mnibbles += 4; |
111 | | offset += (mnibbles as usize) * 4; |
112 | | bytes >>= mnibbles * 4; |
113 | | offset += 1; |
114 | | if (bytes & 1) == 0 { |
115 | | // not UNCOMPRESSED |
116 | | Err(()) // not valid bitstream for concatenation |
117 | | } else { |
118 | | // UNCOMPRESSED: now things are aligend |
119 | | Ok(offset) |
120 | | } |
121 | | } |
122 | | |
123 | | // eat your vegetables |
124 | | #[derive(Default)] |
125 | | pub struct BroCatli { |
126 | | last_bytes: [u8; 2], |
127 | | last_bytes_len: u8, |
128 | | last_byte_sanitized: bool, |
129 | | any_bytes_emitted: bool, |
130 | | last_byte_bit_offset: u8, |
131 | | // need to make sure that window sizes stay similar or get smaller |
132 | | window_size: u8, |
133 | | new_stream_pending: Option<NewStreamData>, |
134 | | } |
135 | | |
136 | | impl BroCatli { |
137 | | pub fn new() -> Self { |
138 | | Self::default() |
139 | | } |
140 | | |
141 | | pub fn deserialize_from_buffer(buffer: &[u8]) -> Result<BroCatli, ()> { |
142 | | if 16 + NUM_STREAM_HEADER_BYTES > buffer.len() { |
143 | | return Err(()); |
144 | | } |
145 | | let mut possible_new_stream_pending = NewStreamData { |
146 | | num_bytes_read: buffer[12], |
147 | | num_bytes_written: if (buffer[9] & (1 << 7)) != 0 { |
148 | | Some(buffer[13]) |
149 | | } else { |
150 | | None |
151 | | }, |
152 | | bytes_so_far: [0; NUM_STREAM_HEADER_BYTES], |
153 | | }; |
154 | | let xlen = possible_new_stream_pending.bytes_so_far.len(); |
155 | | possible_new_stream_pending |
156 | | .bytes_so_far |
157 | | .clone_from_slice(&buffer[16..16 + xlen]); |
158 | | let new_stream_pending: Option<NewStreamData> = if (buffer[9] & (1 << 6)) != 0 { |
159 | | Some(possible_new_stream_pending) |
160 | | } else { |
161 | | None |
162 | | }; |
163 | | let mut ret = BroCatli { |
164 | | last_bytes: [0, 0], |
165 | | last_bytes_len: buffer[8], |
166 | | last_byte_sanitized: (buffer[9] & 0x1) != 0, |
167 | | last_byte_bit_offset: buffer[10], |
168 | | any_bytes_emitted: (buffer[9] & (1 << 5)) != 0, |
169 | | window_size: buffer[11], |
170 | | new_stream_pending, |
171 | | }; |
172 | | if ret.last_bytes.len() > 8 { |
173 | | return Err(()); |
174 | | } |
175 | | let xlen = ret.last_bytes.len(); |
176 | | ret.last_bytes.clone_from_slice(&buffer[..xlen]); |
177 | | Ok(ret) |
178 | | } |
179 | | #[inline(always)] |
180 | 0 | pub fn serialize_to_buffer(&self, buffer: &mut [u8]) -> Result<(), ()> { |
181 | 0 | if 16 + NUM_STREAM_HEADER_BYTES > buffer.len() { |
182 | 0 | return Err(()); |
183 | 0 | } |
184 | 0 | buffer[..self.last_bytes.len()].clone_from_slice(&self.last_bytes[..]); |
185 | 0 | buffer[8] = self.last_bytes_len; |
186 | 0 | buffer[9] = (self.last_byte_sanitized as u8) |
187 | 0 | | ((self.new_stream_pending.is_some() as u8) << 6) |
188 | 0 | | ((self.any_bytes_emitted as u8) << 5); |
189 | 0 | buffer[10] = self.last_byte_bit_offset; |
190 | 0 | buffer[11] = self.window_size; |
191 | 0 | if let Some(new_stream_pending) = self.new_stream_pending { |
192 | 0 | if new_stream_pending.num_bytes_written.is_some() { |
193 | 0 | buffer[9] |= (1 << 7); |
194 | 0 | } |
195 | 0 | buffer[12] = new_stream_pending.num_bytes_read; |
196 | 0 | buffer[13] = new_stream_pending.num_bytes_written.unwrap_or(0); |
197 | 0 | // 14, 15 reserved |
198 | 0 | buffer[16..16 + new_stream_pending.bytes_so_far.len()] |
199 | 0 | .clone_from_slice(&new_stream_pending.bytes_so_far[..]); |
200 | 0 | } |
201 | 0 | Ok(()) |
202 | 0 | } |
203 | | pub fn new_with_window_size(log_window_size: u8) -> BroCatli { |
204 | | // in this case setup the last_bytes of the stream to perfectly mimic what would |
205 | | // appear in an empty stream with the selected window size... |
206 | | // this means the window size followed by 2 sequential 1 bits (LAST_METABLOCK, EMPTY) |
207 | | // the new_stream code should naturally find the sequential 1 bits and mask them |
208 | | // out and then prepend the window size... then the following window sizes should |
209 | | // be checked to be shorter |
210 | | let last_bytes_len; |
211 | | let last_bytes; |
212 | | |
213 | | if log_window_size > 24 { |
214 | | last_bytes = [17u8, log_window_size | 64 | 128]; |
215 | | last_bytes_len = 2; |
216 | | } else if log_window_size == 16 { |
217 | | last_bytes = [1 | 2 | 4, 0]; |
218 | | last_bytes_len = 1; |
219 | | } else if log_window_size > 17 { |
220 | | last_bytes = [(3 + (log_window_size - 18) * 2) | (16 | 32), 0]; |
221 | | last_bytes_len = 1; |
222 | | } else { |
223 | | match log_window_size { |
224 | | 15 => last_bytes = [0x71 | 0x80, 1], |
225 | | 14 => last_bytes = [0x61 | 0x80, 1], |
226 | | 13 => last_bytes = [0x51 | 0x80, 1], |
227 | | 12 => last_bytes = [0x41 | 0x80, 1], |
228 | | 11 => last_bytes = [0x31 | 0x80, 1], |
229 | | 10 => last_bytes = [0x21 | 0x80, 1], |
230 | | _ => { |
231 | | assert_eq!(log_window_size, 17); |
232 | | last_bytes = [0x1 | 0x80, 1]; |
233 | | } // 17 |
234 | | } |
235 | | last_bytes_len = 2; |
236 | | } |
237 | | BroCatli { |
238 | | last_bytes, |
239 | | last_bytes_len, |
240 | | last_byte_bit_offset: 0, |
241 | | last_byte_sanitized: false, |
242 | | any_bytes_emitted: false, |
243 | | new_stream_pending: None, |
244 | | window_size: log_window_size, |
245 | | } |
246 | | } |
247 | | |
248 | | pub fn new_brotli_file(&mut self) { |
249 | | self.new_stream_pending = Some(NewStreamData::new()); |
250 | | } |
251 | | fn flush_previous_stream( |
252 | | &mut self, |
253 | | out_bytes: &mut [u8], |
254 | | out_offset: &mut usize, |
255 | | ) -> BroCatliResult { |
256 | | if !self.last_byte_sanitized { |
257 | | // if the previous stream hasn't had the last metablock (bit 1,1) sanitized |
258 | | if self.last_bytes_len == 0 { |
259 | | // first stream or otherwise sanitized |
260 | | self.last_byte_sanitized = true; |
261 | | return BroCatliResult::Success; |
262 | | } |
263 | | // create a 16 bit integer with the last 2 bytes of data |
264 | | let mut last_bytes = self.last_bytes[0] as u16 + ((self.last_bytes[1] as u16) << 8); |
265 | | let max = self.last_bytes_len * 8; |
266 | | let mut index = max - 1; |
267 | | for i in 0..max { |
268 | | index = max - 1 - i; |
269 | | if ((1 << index) & last_bytes) != 0 { |
270 | | break; // find the highest set bit |
271 | | } |
272 | | } |
273 | | if index == 0 { |
274 | | // if the bit is too low, return failure, since both bits could not possibly have been set |
275 | | return BroCatliResult::BrotliFileNotCraftedForAppend; |
276 | | } |
277 | | if (last_bytes >> (index - 1)) != 3 { |
278 | | // last two bits need to be set for the final metablock |
279 | | return BroCatliResult::BrotliFileNotCraftedForAppend; |
280 | | } |
281 | | index -= 1; // discard the final two bits |
282 | | last_bytes &= (1 << index) - 1; // mask them out |
283 | | self.last_bytes[0] = last_bytes as u8; // reset the last_bytes pair |
284 | | self.last_bytes[1] = (last_bytes >> 8) as u8; |
285 | | if index >= 8 { |
286 | | // if both bits and one useful bit were in the second block, then write that |
287 | | if out_bytes.len() > *out_offset { |
288 | | out_bytes[*out_offset] = self.last_bytes[0]; |
289 | | self.last_bytes[0] = self.last_bytes[1]; |
290 | | *out_offset += 1; |
291 | | self.any_bytes_emitted = true; |
292 | | index -= 8; |
293 | | self.last_bytes_len -= 1; |
294 | | } else { |
295 | | return BroCatliResult::NeedsMoreOutput; |
296 | | } |
297 | | } |
298 | | self.last_byte_bit_offset = index; |
299 | | assert!(index < 8); |
300 | | self.last_byte_sanitized = true; |
301 | | } |
302 | | BroCatliResult::Success |
303 | | } |
304 | | |
305 | | fn shift_and_check_new_stream_header( |
306 | | &mut self, |
307 | | mut new_stream_pending: NewStreamData, |
308 | | out_bytes: &mut [u8], |
309 | | out_offset: &mut usize, |
310 | | ) -> BroCatliResult { |
311 | | if new_stream_pending.num_bytes_written.is_none() { |
312 | | let (window_size, window_offset) = if let Ok(results) = parse_window_size( |
313 | | &new_stream_pending.bytes_so_far[..usize::from(new_stream_pending.num_bytes_read)], |
314 | | ) { |
315 | | results |
316 | | } else { |
317 | | return BroCatliResult::InvalidWindowSize; |
318 | | }; |
319 | | if self.window_size == 0 { |
320 | | // parse window size and just copy everything |
321 | | self.window_size = window_size; |
322 | | assert_eq!(self.last_byte_bit_offset, 0); // we are first stream |
323 | | out_bytes[*out_offset] = new_stream_pending.bytes_so_far[0]; |
324 | | new_stream_pending.num_bytes_written = Some(1); |
325 | | self.any_bytes_emitted = true; |
326 | | *out_offset += 1; |
327 | | } else { |
328 | | if window_size > self.window_size { |
329 | | return BroCatliResult::WindowSizeLargerThanPreviousFile; |
330 | | } |
331 | | let mut realigned_header: [u8; NUM_STREAM_HEADER_BYTES + 1] = |
332 | | [self.last_bytes[0], 0, 0, 0, 0, 0]; |
333 | | let varlen_offset = if let Ok(voffset) = detect_varlen_offset( |
334 | | &new_stream_pending.bytes_so_far |
335 | | [..usize::from(new_stream_pending.num_bytes_read)], |
336 | | ) { |
337 | | voffset |
338 | | } else { |
339 | | return BroCatliResult::BrotliFileNotCraftedForConcatenation; |
340 | | }; |
341 | | let mut bytes_so_far = 0u64; |
342 | | for index in 0..usize::from(new_stream_pending.num_bytes_read) { |
343 | | bytes_so_far |= |
344 | | u64::from(new_stream_pending.bytes_so_far[index]) << (index * 8); |
345 | | } |
346 | | bytes_so_far >>= window_offset; // mask out the window size |
347 | | bytes_so_far &= (1u64 << (varlen_offset - window_offset)) - 1; |
348 | | let var_len_bytes = (((varlen_offset - window_offset) + 7) / 8); |
349 | | for byte_index in 0..var_len_bytes { |
350 | | let cur_byte = (bytes_so_far >> (byte_index * 8)); |
351 | | realigned_header[byte_index] |= |
352 | | ((cur_byte & ((1 << (8 - self.last_byte_bit_offset)) - 1)) |
353 | | << self.last_byte_bit_offset) as u8; |
354 | | realigned_header[byte_index + 1] = |
355 | | (cur_byte >> (8 - self.last_byte_bit_offset)) as u8; |
356 | | } |
357 | | let whole_byte_destination = |
358 | | ((usize::from(self.last_byte_bit_offset) + varlen_offset - window_offset) + 7) |
359 | | / 8; |
360 | | let whole_byte_source = (varlen_offset + 7) / 8; |
361 | | let num_whole_bytes_to_copy = |
362 | | usize::from(new_stream_pending.num_bytes_read) - whole_byte_source; |
363 | | for aligned_index in 0..num_whole_bytes_to_copy { |
364 | | realigned_header[whole_byte_destination + aligned_index] = |
365 | | new_stream_pending.bytes_so_far[whole_byte_source + aligned_index]; |
366 | | } |
367 | | out_bytes[*out_offset] = realigned_header[0]; |
368 | | self.any_bytes_emitted = true; |
369 | | *out_offset += 1; |
370 | | // subtract one since that has just been written out and we're only copying realigned_header[1..] |
371 | | new_stream_pending.num_bytes_read = |
372 | | (whole_byte_destination + num_whole_bytes_to_copy) as u8 - 1; |
373 | | new_stream_pending.num_bytes_written = Some(0); |
374 | | new_stream_pending |
375 | | .bytes_so_far |
376 | | .clone_from_slice(&realigned_header[1..]); |
377 | | } |
378 | | } else { |
379 | | assert_ne!(self.window_size, 0); |
380 | | } |
381 | | let to_copy = min( |
382 | | out_bytes.len() - *out_offset, |
383 | | usize::from( |
384 | | new_stream_pending.num_bytes_read - new_stream_pending.num_bytes_written.unwrap(), |
385 | | ), |
386 | | ); |
387 | | out_bytes |
388 | | .split_at_mut(*out_offset) |
389 | | .1 |
390 | | .split_at_mut(to_copy) |
391 | | .0 |
392 | | .clone_from_slice( |
393 | | new_stream_pending |
394 | | .bytes_so_far |
395 | | .split_at(usize::from(new_stream_pending.num_bytes_written.unwrap())) |
396 | | .1 |
397 | | .split_at(to_copy) |
398 | | .0, |
399 | | ); |
400 | | *out_offset += to_copy; |
401 | | if to_copy != 0 { |
402 | | self.any_bytes_emitted = true; |
403 | | } |
404 | | new_stream_pending.num_bytes_written = |
405 | | Some((new_stream_pending.num_bytes_written.unwrap() + to_copy as u8)); |
406 | | if new_stream_pending.num_bytes_written.unwrap() != new_stream_pending.num_bytes_read { |
407 | | self.new_stream_pending = Some(new_stream_pending); |
408 | | return BroCatliResult::NeedsMoreOutput; |
409 | | } |
410 | | self.new_stream_pending = None; |
411 | | self.last_byte_sanitized = false; |
412 | | self.last_byte_bit_offset = 0; |
413 | | self.last_bytes_len = 0; |
414 | | self.last_bytes = [0, 0]; |
415 | | //now unwrite from the stream, since the last byte may need to be adjusted to be EOF |
416 | | *out_offset -= 1; |
417 | | self.last_bytes[0] = out_bytes[*out_offset]; |
418 | | self.last_bytes_len = 1; |
419 | | BroCatliResult::Success |
420 | | } |
421 | | pub fn stream( |
422 | | &mut self, |
423 | | in_bytes: &[u8], |
424 | | in_offset: &mut usize, |
425 | | out_bytes: &mut [u8], |
426 | | out_offset: &mut usize, |
427 | | ) -> BroCatliResult { |
428 | | if let Some(mut new_stream_pending) = self.new_stream_pending { |
429 | | let flush_result = self.flush_previous_stream(out_bytes, out_offset); |
430 | | if let BroCatliResult::Success = flush_result { |
431 | | if usize::from(new_stream_pending.num_bytes_read) |
432 | | < new_stream_pending.bytes_so_far.len() |
433 | | { |
434 | | { |
435 | | let dst = &mut new_stream_pending.bytes_so_far |
436 | | [usize::from(new_stream_pending.num_bytes_read)..]; |
437 | | let to_copy = min(dst.len(), in_bytes.len() - *in_offset); |
438 | | dst[..to_copy] |
439 | | .clone_from_slice(in_bytes.split_at(*in_offset).1.split_at(to_copy).0); |
440 | | *in_offset += to_copy; |
441 | | new_stream_pending.num_bytes_read += to_copy as u8; |
442 | | } |
443 | | self.new_stream_pending = Some(new_stream_pending); // write back changes |
444 | | } |
445 | | if !new_stream_pending.sufficient() { |
446 | | return BroCatliResult::NeedsMoreInput; |
447 | | } |
448 | | if out_bytes.len() == *out_offset { |
449 | | return BroCatliResult::NeedsMoreOutput; |
450 | | } |
451 | | let shift_result = self.shift_and_check_new_stream_header( |
452 | | new_stream_pending, |
453 | | out_bytes, |
454 | | out_offset, |
455 | | ); |
456 | | if let BroCatliResult::Success = shift_result { |
457 | | } else { |
458 | | return shift_result; |
459 | | } |
460 | | } else { |
461 | | return flush_result; |
462 | | } |
463 | | if *out_offset == out_bytes.len() { |
464 | | return BroCatliResult::NeedsMoreOutput; // need to be able to write at least one byte of data to make progress |
465 | | } |
466 | | } |
467 | | assert!(self.new_stream_pending.is_none()); // this should have been handled above |
468 | | if self.last_bytes_len != 2 { |
469 | | if out_bytes.len() == *out_offset { |
470 | | return BroCatliResult::NeedsMoreOutput; |
471 | | } |
472 | | if in_bytes.len() == *in_offset { |
473 | | return BroCatliResult::NeedsMoreInput; |
474 | | } |
475 | | self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset]; |
476 | | *in_offset += 1; |
477 | | self.last_bytes_len += 1; |
478 | | if self.last_bytes_len != 2 { |
479 | | if out_bytes.len() == *out_offset { |
480 | | return BroCatliResult::NeedsMoreOutput; |
481 | | } |
482 | | if in_bytes.len() == *in_offset { |
483 | | return BroCatliResult::NeedsMoreInput; |
484 | | } |
485 | | self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset]; |
486 | | self.last_bytes_len += 1; |
487 | | *in_offset += 1; |
488 | | } |
489 | | } |
490 | | if out_bytes.len() == *out_offset { |
491 | | return BroCatliResult::NeedsMoreOutput; |
492 | | } |
493 | | if in_bytes.len() == *in_offset { |
494 | | return BroCatliResult::NeedsMoreInput; |
495 | | } |
496 | | let mut to_copy = min(out_bytes.len() - *out_offset, in_bytes.len() - *in_offset); |
497 | | assert_ne!(to_copy, 0); |
498 | | if to_copy == 1 { |
499 | | out_bytes[*out_offset] = self.last_bytes[0]; |
500 | | self.last_bytes[0] = self.last_bytes[1]; |
501 | | self.last_bytes[1] = in_bytes[*in_offset]; |
502 | | *in_offset += 1; |
503 | | *out_offset += 1; |
504 | | if *out_offset == out_bytes.len() { |
505 | | return BroCatliResult::NeedsMoreOutput; |
506 | | } |
507 | | return BroCatliResult::NeedsMoreInput; |
508 | | } |
509 | | out_bytes |
510 | | .split_at_mut(*out_offset) |
511 | | .1 |
512 | | .split_at_mut(2) |
513 | | .0 |
514 | | .clone_from_slice(&self.last_bytes[..]); |
515 | | *out_offset += 2; |
516 | | let (new_in_offset, last_two) = in_bytes |
517 | | .split_at(*in_offset) |
518 | | .1 |
519 | | .split_at(to_copy) |
520 | | .0 |
521 | | .split_at(to_copy - 2); |
522 | | self.last_bytes.clone_from_slice(last_two); |
523 | | *in_offset += 2; // add this after the clone since we grab the last 2 bytes, not the first |
524 | | to_copy -= 2; |
525 | | out_bytes |
526 | | .split_at_mut(*out_offset) |
527 | | .1 |
528 | | .split_at_mut(to_copy) |
529 | | .0 |
530 | | .clone_from_slice(new_in_offset); |
531 | | *out_offset += to_copy; |
532 | | *in_offset += to_copy; |
533 | | if *out_offset == out_bytes.len() { |
534 | | return BroCatliResult::NeedsMoreOutput; |
535 | | } |
536 | | BroCatliResult::NeedsMoreInput |
537 | | } |
538 | | fn append_eof_metablock_to_last_bytes(&mut self) { |
539 | | assert!(self.last_byte_sanitized); |
540 | | let mut last_bytes = self.last_bytes[0] as u16 | ((self.last_bytes[1] as u16) << 8); |
541 | | let bit_end = (self.last_bytes_len - 1) * 8 + self.last_byte_bit_offset; |
542 | | last_bytes |= 3 << bit_end; |
543 | | self.last_bytes[0] = last_bytes as u8; |
544 | | self.last_bytes[1] = (last_bytes >> 8) as u8; |
545 | | self.last_byte_sanitized = false; |
546 | | self.last_byte_bit_offset += 2; |
547 | | if self.last_byte_bit_offset >= 8 { |
548 | | self.last_byte_bit_offset -= 8; |
549 | | self.last_bytes_len += 1; |
550 | | } |
551 | | } |
552 | | pub fn finish(&mut self, out_bytes: &mut [u8], out_offset: &mut usize) -> BroCatliResult { |
553 | | if self.last_byte_sanitized && self.last_bytes_len != 0 { |
554 | | self.append_eof_metablock_to_last_bytes(); |
555 | | } |
556 | | while self.last_bytes_len != 0 { |
557 | | if *out_offset == out_bytes.len() { |
558 | | return BroCatliResult::NeedsMoreOutput; |
559 | | } |
560 | | out_bytes[*out_offset] = self.last_bytes[0]; |
561 | | *out_offset += 1; |
562 | | self.last_bytes_len -= 1; |
563 | | self.last_bytes[0] = self.last_bytes[1]; |
564 | | self.any_bytes_emitted = true; |
565 | | } |
566 | | if !self.any_bytes_emitted { |
567 | | if out_bytes.len() == *out_offset { |
568 | | return BroCatliResult::NeedsMoreOutput; |
569 | | } |
570 | | self.any_bytes_emitted = true; |
571 | | out_bytes[*out_offset] = b';'; |
572 | | *out_offset += 1; |
573 | | } |
574 | | BroCatliResult::Success |
575 | | } |
576 | | } |
577 | | |
578 | | #[cfg(test)] |
579 | | mod test { |
580 | | use super::BroCatli; |
581 | | |
582 | | #[test] |
583 | | fn test_deserialization() { |
584 | | let broccoli = BroCatli { |
585 | | new_stream_pending: Some(super::NewStreamData { |
586 | | bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES], |
587 | | num_bytes_read: 16, |
588 | | num_bytes_written: Some(3), |
589 | | }), |
590 | | last_bytes: [0x45, 0x46], |
591 | | last_bytes_len: 1, |
592 | | last_byte_sanitized: true, |
593 | | any_bytes_emitted: false, |
594 | | last_byte_bit_offset: 7, |
595 | | window_size: 22, |
596 | | }; |
597 | | let mut buffer = [0u8; 248]; |
598 | | broccoli.serialize_to_buffer(&mut buffer[..]).unwrap(); |
599 | | let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap(); |
600 | | assert_eq!(broccoli.last_bytes, bc.last_bytes); |
601 | | assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len); |
602 | | assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized); |
603 | | assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset); |
604 | | assert_eq!(broccoli.window_size, bc.window_size); |
605 | | assert_eq!( |
606 | | broccoli.new_stream_pending.unwrap().bytes_so_far, |
607 | | bc.new_stream_pending.unwrap().bytes_so_far |
608 | | ); |
609 | | assert_eq!( |
610 | | broccoli.new_stream_pending.unwrap().num_bytes_read, |
611 | | bc.new_stream_pending.unwrap().num_bytes_read |
612 | | ); |
613 | | assert_eq!( |
614 | | broccoli.new_stream_pending.unwrap().num_bytes_written, |
615 | | bc.new_stream_pending.unwrap().num_bytes_written |
616 | | ); |
617 | | } |
618 | | #[test] |
619 | | fn test_deserialization_any_written() { |
620 | | let broccoli = BroCatli { |
621 | | new_stream_pending: Some(super::NewStreamData { |
622 | | bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES], |
623 | | num_bytes_read: 16, |
624 | | num_bytes_written: Some(3), |
625 | | }), |
626 | | last_bytes: [0x45, 0x46], |
627 | | last_bytes_len: 1, |
628 | | last_byte_sanitized: true, |
629 | | any_bytes_emitted: true, |
630 | | last_byte_bit_offset: 7, |
631 | | window_size: 22, |
632 | | }; |
633 | | let mut buffer = [0u8; 248]; |
634 | | broccoli.serialize_to_buffer(&mut buffer[..]).unwrap(); |
635 | | let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap(); |
636 | | assert_eq!(broccoli.last_bytes, bc.last_bytes); |
637 | | assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len); |
638 | | assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized); |
639 | | assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset); |
640 | | assert_eq!(broccoli.window_size, bc.window_size); |
641 | | assert_eq!( |
642 | | broccoli.new_stream_pending.unwrap().bytes_so_far, |
643 | | bc.new_stream_pending.unwrap().bytes_so_far |
644 | | ); |
645 | | assert_eq!( |
646 | | broccoli.new_stream_pending.unwrap().num_bytes_read, |
647 | | bc.new_stream_pending.unwrap().num_bytes_read |
648 | | ); |
649 | | assert_eq!( |
650 | | broccoli.new_stream_pending.unwrap().num_bytes_written, |
651 | | bc.new_stream_pending.unwrap().num_bytes_written |
652 | | ); |
653 | | } |
654 | | #[test] |
655 | | fn test_serialization() { |
656 | | let mut buffer = [0u8; 248]; |
657 | | let mut broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap(); |
658 | | let mut buffer2 = [0u8; 248]; |
659 | | broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap(); |
660 | | assert_eq!(&buffer[..], &buffer2[..]); |
661 | | for (index, item) in buffer.iter_mut().enumerate() { |
662 | | *item = index as u8; |
663 | | } |
664 | | broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap(); |
665 | | broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap(); |
666 | | broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap(); |
667 | | for (_index, item) in buffer.iter_mut().enumerate() { |
668 | | *item = 0; |
669 | | } |
670 | | broccoli.serialize_to_buffer(&mut buffer[..]).unwrap(); |
671 | | assert_eq!(&buffer[..], &buffer2[..]); |
672 | | for (index, item) in buffer.iter_mut().enumerate() { |
673 | | *item = 0xff ^ index as u8; |
674 | | } |
675 | | broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap(); |
676 | | broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap(); |
677 | | broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap(); |
678 | | for (_index, item) in buffer.iter_mut().enumerate() { |
679 | | *item = 0; |
680 | | } |
681 | | broccoli.serialize_to_buffer(&mut buffer[..]).unwrap(); |
682 | | assert_eq!(&buffer[..], &buffer2[..]); |
683 | | } |
684 | | #[test] |
685 | | fn test_cat_empty_stream() { |
686 | | let empty_catable = [b';']; |
687 | | let mut bcat = super::BroCatli::default(); |
688 | | let mut in_offset = 0usize; |
689 | | let mut out_bytes = [0u8; 32]; |
690 | | let mut out_offset = 0usize; |
691 | | bcat.new_brotli_file(); |
692 | | let mut res = bcat.stream( |
693 | | &empty_catable[..], |
694 | | &mut in_offset, |
695 | | &mut out_bytes[..], |
696 | | &mut out_offset, |
697 | | ); |
698 | | assert_eq!(res, super::BroCatliResult::NeedsMoreInput); |
699 | | bcat.new_brotli_file(); |
700 | | in_offset = 0; |
701 | | res = bcat.stream( |
702 | | &empty_catable[..], |
703 | | &mut in_offset, |
704 | | &mut out_bytes[..], |
705 | | &mut out_offset, |
706 | | ); |
707 | | assert_eq!(res, super::BroCatliResult::NeedsMoreInput); |
708 | | res = bcat.finish(&mut out_bytes[..], &mut out_offset); |
709 | | assert_eq!(res, super::BroCatliResult::Success); |
710 | | assert_ne!(out_offset, 0); |
711 | | assert_eq!(&out_bytes[..out_offset], &[b';']); |
712 | | } |
713 | | } |