1use core::cmp::min;
2
3#[repr(C)]
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub 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
15const NUM_STREAM_HEADER_BYTES: usize = 5;
16
17#[derive(Clone, Copy)]
18struct NewStreamData {
19 bytes_so_far: [u8; NUM_STREAM_HEADER_BYTES],
20 num_bytes_read: u8,
21 num_bytes_written: Option<u8>,
22}
23impl 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
39fn parse_window_size(bytes_so_far: &[u8]) -> Result<(u8, usize), ()> {
40 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
73fn detect_varlen_offset(bytes_so_far: &[u8]) -> Result<(usize), ()> {
74 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 bytes >>= 1;
88 offset += 1;
89 if (bytes & 1) != 0 {
90 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 if (bytes & 1) != 0 {
101 return Err(()); }
103 bytes >>= 1;
104 offset += 1;
105 let mskipbytes = bytes & ((1 << 2) - 1);
106 offset += 2;
107 offset += (mskipbytes as usize) * 8; 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 Err(()) } else {
118 Ok(offset)
120 }
121}
122
123#[derive(Default)]
125pub 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 window_size: u8,
133 new_stream_pending: Option<NewStreamData>,
134}
135
136impl 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 pub fn serialize_to_buffer(&self, buffer: &mut [u8]) -> Result<(), ()> {
181 if 16 + NUM_STREAM_HEADER_BYTES > buffer.len() {
182 return Err(());
183 }
184 buffer[..self.last_bytes.len()].clone_from_slice(&self.last_bytes[..]);
185 buffer[8] = self.last_bytes_len;
186 buffer[9] = (self.last_byte_sanitized as u8)
187 | ((self.new_stream_pending.is_some() as u8) << 6)
188 | ((self.any_bytes_emitted as u8) << 5);
189 buffer[10] = self.last_byte_bit_offset;
190 buffer[11] = self.window_size;
191 if let Some(new_stream_pending) = self.new_stream_pending {
192 if new_stream_pending.num_bytes_written.is_some() {
193 buffer[9] |= (1 << 7);
194 }
195 buffer[12] = new_stream_pending.num_bytes_read;
196 buffer[13] = new_stream_pending.num_bytes_written.unwrap_or(0);
197 buffer[16..16 + new_stream_pending.bytes_so_far.len()]
199 .clone_from_slice(&new_stream_pending.bytes_so_far[..]);
200 }
201 Ok(())
202 }
203 pub fn new_with_window_size(log_window_size: u8) -> BroCatli {
208 Self::try_new_with_window_size(log_window_size).expect("invalid brotli window size")
209 }
210
211 pub fn try_new_with_window_size(log_window_size: u8) -> Result<BroCatli, BroCatliResult> {
213 let last_bytes_len;
220 let last_bytes;
221
222 if log_window_size > 24 {
223 last_bytes = [17u8, log_window_size | 64 | 128];
224 last_bytes_len = 2;
225 } else if log_window_size == 16 {
226 last_bytes = [1 | 2 | 4, 0];
227 last_bytes_len = 1;
228 } else if log_window_size > 17 {
229 last_bytes = [(3 + (log_window_size - 18) * 2) | (16 | 32), 0];
230 last_bytes_len = 1;
231 } else {
232 match log_window_size {
233 15 => last_bytes = [0x71 | 0x80, 1],
234 14 => last_bytes = [0x61 | 0x80, 1],
235 13 => last_bytes = [0x51 | 0x80, 1],
236 12 => last_bytes = [0x41 | 0x80, 1],
237 11 => last_bytes = [0x31 | 0x80, 1],
238 10 => last_bytes = [0x21 | 0x80, 1],
239 17 => last_bytes = [0x1 | 0x80, 1],
240 _ => return Err(BroCatliResult::InvalidWindowSize),
241 }
242 last_bytes_len = 2;
243 }
244 Ok(BroCatli {
245 last_bytes,
246 last_bytes_len,
247 last_byte_bit_offset: 0,
248 last_byte_sanitized: false,
249 any_bytes_emitted: false,
250 new_stream_pending: None,
251 window_size: log_window_size,
252 })
253 }
254
255 pub fn new_brotli_file(&mut self) {
256 self.new_stream_pending = Some(NewStreamData::new());
257 }
258 fn flush_previous_stream(
259 &mut self,
260 out_bytes: &mut [u8],
261 out_offset: &mut usize,
262 ) -> BroCatliResult {
263 if !self.last_byte_sanitized {
264 if self.last_bytes_len == 0 {
266 self.last_byte_sanitized = true;
268 return BroCatliResult::Success;
269 }
270 let mut last_bytes = self.last_bytes[0] as u16 + ((self.last_bytes[1] as u16) << 8);
272 let max = self.last_bytes_len * 8;
273 let mut index = max - 1;
274 for i in 0..max {
275 index = max - 1 - i;
276 if ((1 << index) & last_bytes) != 0 {
277 break; }
279 }
280 if index == 0 {
281 return BroCatliResult::BrotliFileNotCraftedForAppend;
283 }
284 if (last_bytes >> (index - 1)) != 3 {
285 return BroCatliResult::BrotliFileNotCraftedForAppend;
287 }
288 index -= 1; last_bytes &= (1 << index) - 1; self.last_bytes[0] = last_bytes as u8; self.last_bytes[1] = (last_bytes >> 8) as u8;
292 if index >= 8 {
293 if out_bytes.len() > *out_offset {
295 out_bytes[*out_offset] = self.last_bytes[0];
296 self.last_bytes[0] = self.last_bytes[1];
297 *out_offset += 1;
298 self.any_bytes_emitted = true;
299 index -= 8;
300 self.last_bytes_len -= 1;
301 } else {
302 return BroCatliResult::NeedsMoreOutput;
303 }
304 }
305 self.last_byte_bit_offset = index;
306 assert!(index < 8);
307 self.last_byte_sanitized = true;
308 }
309 BroCatliResult::Success
310 }
311
312 fn shift_and_check_new_stream_header(
313 &mut self,
314 mut new_stream_pending: NewStreamData,
315 out_bytes: &mut [u8],
316 out_offset: &mut usize,
317 ) -> BroCatliResult {
318 if new_stream_pending.num_bytes_written.is_none() {
319 let (window_size, window_offset) = if let Ok(results) = parse_window_size(
320 &new_stream_pending.bytes_so_far[..usize::from(new_stream_pending.num_bytes_read)],
321 ) {
322 results
323 } else {
324 return BroCatliResult::InvalidWindowSize;
325 };
326 if self.window_size == 0 {
327 self.window_size = window_size;
329 assert_eq!(self.last_byte_bit_offset, 0); out_bytes[*out_offset] = new_stream_pending.bytes_so_far[0];
331 new_stream_pending.num_bytes_written = Some(1);
332 self.any_bytes_emitted = true;
333 *out_offset += 1;
334 } else {
335 if window_size > self.window_size {
336 return BroCatliResult::WindowSizeLargerThanPreviousFile;
337 }
338 let mut realigned_header: [u8; NUM_STREAM_HEADER_BYTES + 1] =
339 [self.last_bytes[0], 0, 0, 0, 0, 0];
340 let varlen_offset = if let Ok(voffset) = detect_varlen_offset(
341 &new_stream_pending.bytes_so_far
342 [..usize::from(new_stream_pending.num_bytes_read)],
343 ) {
344 voffset
345 } else {
346 return BroCatliResult::BrotliFileNotCraftedForConcatenation;
347 };
348 let mut bytes_so_far = 0u64;
349 for index in 0..usize::from(new_stream_pending.num_bytes_read) {
350 bytes_so_far |=
351 u64::from(new_stream_pending.bytes_so_far[index]) << (index * 8);
352 }
353 bytes_so_far >>= window_offset; bytes_so_far &= (1u64 << (varlen_offset - window_offset)) - 1;
355 let var_len_bytes = (((varlen_offset - window_offset) + 7) / 8);
356 for byte_index in 0..var_len_bytes {
357 let cur_byte = (bytes_so_far >> (byte_index * 8));
358 realigned_header[byte_index] |=
359 ((cur_byte & ((1 << (8 - self.last_byte_bit_offset)) - 1))
360 << self.last_byte_bit_offset) as u8;
361 realigned_header[byte_index + 1] =
362 (cur_byte >> (8 - self.last_byte_bit_offset)) as u8;
363 }
364 let whole_byte_destination =
365 ((usize::from(self.last_byte_bit_offset) + varlen_offset - window_offset) + 7)
366 / 8;
367 let whole_byte_source = (varlen_offset + 7) / 8;
368 let num_whole_bytes_to_copy =
369 usize::from(new_stream_pending.num_bytes_read) - whole_byte_source;
370 for aligned_index in 0..num_whole_bytes_to_copy {
371 realigned_header[whole_byte_destination + aligned_index] =
372 new_stream_pending.bytes_so_far[whole_byte_source + aligned_index];
373 }
374 out_bytes[*out_offset] = realigned_header[0];
375 self.any_bytes_emitted = true;
376 *out_offset += 1;
377 new_stream_pending.num_bytes_read =
379 (whole_byte_destination + num_whole_bytes_to_copy) as u8 - 1;
380 new_stream_pending.num_bytes_written = Some(0);
381 new_stream_pending
382 .bytes_so_far
383 .clone_from_slice(&realigned_header[1..]);
384 }
385 } else {
386 assert_ne!(self.window_size, 0);
387 }
388 let to_copy = min(
389 out_bytes.len() - *out_offset,
390 usize::from(
391 new_stream_pending.num_bytes_read - new_stream_pending.num_bytes_written.unwrap(),
392 ),
393 );
394 out_bytes
395 .split_at_mut(*out_offset)
396 .1
397 .split_at_mut(to_copy)
398 .0
399 .clone_from_slice(
400 new_stream_pending
401 .bytes_so_far
402 .split_at(usize::from(new_stream_pending.num_bytes_written.unwrap()))
403 .1
404 .split_at(to_copy)
405 .0,
406 );
407 *out_offset += to_copy;
408 if to_copy != 0 {
409 self.any_bytes_emitted = true;
410 }
411 new_stream_pending.num_bytes_written =
412 Some((new_stream_pending.num_bytes_written.unwrap() + to_copy as u8));
413 if new_stream_pending.num_bytes_written.unwrap() != new_stream_pending.num_bytes_read {
414 self.new_stream_pending = Some(new_stream_pending);
415 return BroCatliResult::NeedsMoreOutput;
416 }
417 self.new_stream_pending = None;
418 self.last_byte_sanitized = false;
419 self.last_byte_bit_offset = 0;
420 self.last_bytes_len = 0;
421 self.last_bytes = [0, 0];
422 *out_offset -= 1;
424 self.last_bytes[0] = out_bytes[*out_offset];
425 self.last_bytes_len = 1;
426 BroCatliResult::Success
427 }
428 pub fn stream(
429 &mut self,
430 in_bytes: &[u8],
431 in_offset: &mut usize,
432 out_bytes: &mut [u8],
433 out_offset: &mut usize,
434 ) -> BroCatliResult {
435 if let Some(mut new_stream_pending) = self.new_stream_pending {
436 let flush_result = self.flush_previous_stream(out_bytes, out_offset);
437 if let BroCatliResult::Success = flush_result {
438 if usize::from(new_stream_pending.num_bytes_read)
439 < new_stream_pending.bytes_so_far.len()
440 {
441 {
442 let dst = &mut new_stream_pending.bytes_so_far
443 [usize::from(new_stream_pending.num_bytes_read)..];
444 let to_copy = min(dst.len(), in_bytes.len() - *in_offset);
445 dst[..to_copy]
446 .clone_from_slice(in_bytes.split_at(*in_offset).1.split_at(to_copy).0);
447 *in_offset += to_copy;
448 new_stream_pending.num_bytes_read += to_copy as u8;
449 }
450 self.new_stream_pending = Some(new_stream_pending); }
452 if !new_stream_pending.sufficient() {
453 return BroCatliResult::NeedsMoreInput;
454 }
455 if out_bytes.len() == *out_offset {
456 return BroCatliResult::NeedsMoreOutput;
457 }
458 let shift_result = self.shift_and_check_new_stream_header(
459 new_stream_pending,
460 out_bytes,
461 out_offset,
462 );
463 if let BroCatliResult::Success = shift_result {
464 } else {
465 return shift_result;
466 }
467 } else {
468 return flush_result;
469 }
470 if *out_offset == out_bytes.len() {
471 return BroCatliResult::NeedsMoreOutput; }
473 }
474 assert!(self.new_stream_pending.is_none()); if self.last_bytes_len != 2 {
476 if out_bytes.len() == *out_offset {
477 return BroCatliResult::NeedsMoreOutput;
478 }
479 if in_bytes.len() == *in_offset {
480 return BroCatliResult::NeedsMoreInput;
481 }
482 self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset];
483 *in_offset += 1;
484 self.last_bytes_len += 1;
485 if self.last_bytes_len != 2 {
486 if out_bytes.len() == *out_offset {
487 return BroCatliResult::NeedsMoreOutput;
488 }
489 if in_bytes.len() == *in_offset {
490 return BroCatliResult::NeedsMoreInput;
491 }
492 self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset];
493 self.last_bytes_len += 1;
494 *in_offset += 1;
495 }
496 }
497 if out_bytes.len() == *out_offset {
498 return BroCatliResult::NeedsMoreOutput;
499 }
500 if in_bytes.len() == *in_offset {
501 return BroCatliResult::NeedsMoreInput;
502 }
503 let mut to_copy = min(out_bytes.len() - *out_offset, in_bytes.len() - *in_offset);
504 assert_ne!(to_copy, 0);
505 if to_copy == 1 {
506 out_bytes[*out_offset] = self.last_bytes[0];
507 self.last_bytes[0] = self.last_bytes[1];
508 self.last_bytes[1] = in_bytes[*in_offset];
509 *in_offset += 1;
510 *out_offset += 1;
511 if *out_offset == out_bytes.len() {
512 return BroCatliResult::NeedsMoreOutput;
513 }
514 return BroCatliResult::NeedsMoreInput;
515 }
516 out_bytes
517 .split_at_mut(*out_offset)
518 .1
519 .split_at_mut(2)
520 .0
521 .clone_from_slice(&self.last_bytes[..]);
522 *out_offset += 2;
523 let (new_in_offset, last_two) = in_bytes
524 .split_at(*in_offset)
525 .1
526 .split_at(to_copy)
527 .0
528 .split_at(to_copy - 2);
529 self.last_bytes.clone_from_slice(last_two);
530 *in_offset += 2; to_copy -= 2;
532 out_bytes
533 .split_at_mut(*out_offset)
534 .1
535 .split_at_mut(to_copy)
536 .0
537 .clone_from_slice(new_in_offset);
538 *out_offset += to_copy;
539 *in_offset += to_copy;
540 if *out_offset == out_bytes.len() {
541 return BroCatliResult::NeedsMoreOutput;
542 }
543 BroCatliResult::NeedsMoreInput
544 }
545 fn append_eof_metablock_to_last_bytes(&mut self) {
546 assert!(self.last_byte_sanitized);
547 let mut last_bytes = self.last_bytes[0] as u16 | ((self.last_bytes[1] as u16) << 8);
548 let bit_end = (self.last_bytes_len - 1) * 8 + self.last_byte_bit_offset;
549 last_bytes |= 3 << bit_end;
550 self.last_bytes[0] = last_bytes as u8;
551 self.last_bytes[1] = (last_bytes >> 8) as u8;
552 self.last_byte_sanitized = false;
553 self.last_byte_bit_offset += 2;
554 if self.last_byte_bit_offset >= 8 {
555 self.last_byte_bit_offset -= 8;
556 self.last_bytes_len += 1;
557 }
558 }
559 pub fn finish(&mut self, out_bytes: &mut [u8], out_offset: &mut usize) -> BroCatliResult {
560 if self.last_byte_sanitized && self.last_bytes_len != 0 {
561 self.append_eof_metablock_to_last_bytes();
562 }
563 while self.last_bytes_len != 0 {
564 if *out_offset == out_bytes.len() {
565 return BroCatliResult::NeedsMoreOutput;
566 }
567 out_bytes[*out_offset] = self.last_bytes[0];
568 *out_offset += 1;
569 self.last_bytes_len -= 1;
570 self.last_bytes[0] = self.last_bytes[1];
571 self.any_bytes_emitted = true;
572 }
573 if !self.any_bytes_emitted {
574 if out_bytes.len() == *out_offset {
575 return BroCatliResult::NeedsMoreOutput;
576 }
577 self.any_bytes_emitted = true;
578 out_bytes[*out_offset] = b';';
579 *out_offset += 1;
580 }
581 BroCatliResult::Success
582 }
583}
584
585#[cfg(test)]
586mod test {
587 use super::BroCatli;
588
589 #[test]
590 fn test_deserialization() {
591 let broccoli = BroCatli {
592 new_stream_pending: Some(super::NewStreamData {
593 bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES],
594 num_bytes_read: 16,
595 num_bytes_written: Some(3),
596 }),
597 last_bytes: [0x45, 0x46],
598 last_bytes_len: 1,
599 last_byte_sanitized: true,
600 any_bytes_emitted: false,
601 last_byte_bit_offset: 7,
602 window_size: 22,
603 };
604 let mut buffer = [0u8; 248];
605 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
606 let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap();
607 assert_eq!(broccoli.last_bytes, bc.last_bytes);
608 assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len);
609 assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized);
610 assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset);
611 assert_eq!(broccoli.window_size, bc.window_size);
612 assert_eq!(
613 broccoli.new_stream_pending.unwrap().bytes_so_far,
614 bc.new_stream_pending.unwrap().bytes_so_far
615 );
616 assert_eq!(
617 broccoli.new_stream_pending.unwrap().num_bytes_read,
618 bc.new_stream_pending.unwrap().num_bytes_read
619 );
620 assert_eq!(
621 broccoli.new_stream_pending.unwrap().num_bytes_written,
622 bc.new_stream_pending.unwrap().num_bytes_written
623 );
624 }
625 #[test]
626 fn test_deserialization_any_written() {
627 let broccoli = BroCatli {
628 new_stream_pending: Some(super::NewStreamData {
629 bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES],
630 num_bytes_read: 16,
631 num_bytes_written: Some(3),
632 }),
633 last_bytes: [0x45, 0x46],
634 last_bytes_len: 1,
635 last_byte_sanitized: true,
636 any_bytes_emitted: true,
637 last_byte_bit_offset: 7,
638 window_size: 22,
639 };
640 let mut buffer = [0u8; 248];
641 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
642 let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap();
643 assert_eq!(broccoli.last_bytes, bc.last_bytes);
644 assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len);
645 assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized);
646 assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset);
647 assert_eq!(broccoli.window_size, bc.window_size);
648 assert_eq!(
649 broccoli.new_stream_pending.unwrap().bytes_so_far,
650 bc.new_stream_pending.unwrap().bytes_so_far
651 );
652 assert_eq!(
653 broccoli.new_stream_pending.unwrap().num_bytes_read,
654 bc.new_stream_pending.unwrap().num_bytes_read
655 );
656 assert_eq!(
657 broccoli.new_stream_pending.unwrap().num_bytes_written,
658 bc.new_stream_pending.unwrap().num_bytes_written
659 );
660 }
661 #[test]
662 fn test_serialization() {
663 let mut buffer = [0u8; 248];
664 let mut broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
665 let mut buffer2 = [0u8; 248];
666 broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
667 assert_eq!(&buffer[..], &buffer2[..]);
668 for (index, item) in buffer.iter_mut().enumerate() {
669 *item = index as u8;
670 }
671 broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
672 broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
673 broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap();
674 for (_index, item) in buffer.iter_mut().enumerate() {
675 *item = 0;
676 }
677 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
678 assert_eq!(&buffer[..], &buffer2[..]);
679 for (index, item) in buffer.iter_mut().enumerate() {
680 *item = 0xff ^ index as u8;
681 }
682 broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
683 broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
684 broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap();
685 for (_index, item) in buffer.iter_mut().enumerate() {
686 *item = 0;
687 }
688 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
689 assert_eq!(&buffer[..], &buffer2[..]);
690 }
691 #[test]
692 fn test_cat_empty_stream() {
693 let empty_catable = [b';'];
694 let mut bcat = super::BroCatli::default();
695 let mut in_offset = 0usize;
696 let mut out_bytes = [0u8; 32];
697 let mut out_offset = 0usize;
698 bcat.new_brotli_file();
699 let mut res = bcat.stream(
700 &empty_catable[..],
701 &mut in_offset,
702 &mut out_bytes[..],
703 &mut out_offset,
704 );
705 assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
706 bcat.new_brotli_file();
707 in_offset = 0;
708 res = bcat.stream(
709 &empty_catable[..],
710 &mut in_offset,
711 &mut out_bytes[..],
712 &mut out_offset,
713 );
714 assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
715 res = bcat.finish(&mut out_bytes[..], &mut out_offset);
716 assert_eq!(res, super::BroCatliResult::Success);
717 assert_ne!(out_offset, 0);
718 assert_eq!(&out_bytes[..out_offset], &[b';']);
719 }
720 #[test]
721 fn test_try_new_with_window_size_invalid_returns_error() {
722 use super::BroCatliResult;
723 for ws in 0u8..=9 {
725 match BroCatli::try_new_with_window_size(ws) {
726 Err(BroCatliResult::InvalidWindowSize) => {}
727 Err(_) => panic!("window_size {} returned wrong error variant", ws),
728 Ok(_) => panic!("window_size {} should be rejected", ws),
729 }
730 #[cfg(feature = "std")]
731 assert!(
732 std::panic::catch_unwind(|| BroCatli::new_with_window_size(ws)).is_err(),
733 "window_size {} should panic through the legacy constructor",
734 ws
735 );
736 }
737 }
738 #[test]
739 fn test_new_with_window_size_valid() {
740 for ws in 10u8..=30 {
742 let _ = BroCatli::new_with_window_size(ws);
743 assert!(
744 BroCatli::try_new_with_window_size(ws).is_ok(),
745 "window_size {} should be accepted",
746 ws
747 );
748 }
749 }
750}