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 last_bytes_len = buffer[8];
146 let last_byte_bit_offset = buffer[10];
147 let window_size = buffer[11];
148 let has_new_stream_pending = (buffer[9] & (1 << 6)) != 0;
149 let has_num_bytes_written = (buffer[9] & (1 << 7)) != 0;
150 if last_bytes_len > 2 || last_byte_bit_offset >= 8 {
151 return Err(());
152 }
153 if window_size != 0 && BroCatli::try_new_with_window_size(window_size).is_err() {
154 return Err(());
155 }
156 if has_new_stream_pending {
157 if usize::from(buffer[12]) > NUM_STREAM_HEADER_BYTES {
158 return Err(());
159 }
160 if has_num_bytes_written && buffer[13] > buffer[12] {
161 return Err(());
162 }
163 }
164 let mut possible_new_stream_pending = NewStreamData {
165 num_bytes_read: buffer[12],
166 num_bytes_written: if has_num_bytes_written {
167 Some(buffer[13])
168 } else {
169 None
170 },
171 bytes_so_far: [0; NUM_STREAM_HEADER_BYTES],
172 };
173 let xlen = possible_new_stream_pending.bytes_so_far.len();
174 possible_new_stream_pending
175 .bytes_so_far
176 .clone_from_slice(&buffer[16..16 + xlen]);
177 let new_stream_pending: Option<NewStreamData> = if has_new_stream_pending {
178 Some(possible_new_stream_pending)
179 } else {
180 None
181 };
182 let mut ret = BroCatli {
183 last_bytes: [0, 0],
184 last_bytes_len,
185 last_byte_sanitized: (buffer[9] & 0x1) != 0,
186 last_byte_bit_offset,
187 any_bytes_emitted: (buffer[9] & (1 << 5)) != 0,
188 window_size,
189 new_stream_pending,
190 };
191 if ret.last_bytes.len() > 8 {
192 return Err(());
193 }
194 let xlen = ret.last_bytes.len();
195 ret.last_bytes.clone_from_slice(&buffer[..xlen]);
196 Ok(ret)
197 }
198 #[inline(always)]
199 pub fn serialize_to_buffer(&self, buffer: &mut [u8]) -> Result<(), ()> {
200 if 16 + NUM_STREAM_HEADER_BYTES > buffer.len() {
201 return Err(());
202 }
203 buffer[..self.last_bytes.len()].clone_from_slice(&self.last_bytes[..]);
204 buffer[8] = self.last_bytes_len;
205 buffer[9] = (self.last_byte_sanitized as u8)
206 | ((self.new_stream_pending.is_some() as u8) << 6)
207 | ((self.any_bytes_emitted as u8) << 5);
208 buffer[10] = self.last_byte_bit_offset;
209 buffer[11] = self.window_size;
210 if let Some(new_stream_pending) = self.new_stream_pending {
211 if new_stream_pending.num_bytes_written.is_some() {
212 buffer[9] |= (1 << 7);
213 }
214 buffer[12] = new_stream_pending.num_bytes_read;
215 buffer[13] = new_stream_pending.num_bytes_written.unwrap_or(0);
216 buffer[16..16 + new_stream_pending.bytes_so_far.len()]
218 .clone_from_slice(&new_stream_pending.bytes_so_far[..]);
219 }
220 Ok(())
221 }
222 pub fn new_with_window_size(log_window_size: u8) -> BroCatli {
227 Self::try_new_with_window_size(log_window_size).expect("invalid brotli window size")
228 }
229
230 pub fn try_new_with_window_size(log_window_size: u8) -> Result<BroCatli, BroCatliResult> {
232 let last_bytes_len;
239 let last_bytes;
240
241 if log_window_size > 24 {
242 last_bytes = [17u8, log_window_size | 64 | 128];
243 last_bytes_len = 2;
244 } else if log_window_size == 16 {
245 last_bytes = [1 | 2 | 4, 0];
246 last_bytes_len = 1;
247 } else if log_window_size > 17 {
248 last_bytes = [(3 + (log_window_size - 18) * 2) | (16 | 32), 0];
249 last_bytes_len = 1;
250 } else {
251 match log_window_size {
252 15 => last_bytes = [0x71 | 0x80, 1],
253 14 => last_bytes = [0x61 | 0x80, 1],
254 13 => last_bytes = [0x51 | 0x80, 1],
255 12 => last_bytes = [0x41 | 0x80, 1],
256 11 => last_bytes = [0x31 | 0x80, 1],
257 10 => last_bytes = [0x21 | 0x80, 1],
258 17 => last_bytes = [0x1 | 0x80, 1],
259 _ => return Err(BroCatliResult::InvalidWindowSize),
260 }
261 last_bytes_len = 2;
262 }
263 Ok(BroCatli {
264 last_bytes,
265 last_bytes_len,
266 last_byte_bit_offset: 0,
267 last_byte_sanitized: false,
268 any_bytes_emitted: false,
269 new_stream_pending: None,
270 window_size: log_window_size,
271 })
272 }
273
274 pub fn new_brotli_file(&mut self) {
275 self.new_stream_pending = Some(NewStreamData::new());
276 }
277 fn flush_previous_stream(
278 &mut self,
279 out_bytes: &mut [u8],
280 out_offset: &mut usize,
281 ) -> BroCatliResult {
282 if !self.last_byte_sanitized {
283 if self.last_bytes_len == 0 {
285 self.last_byte_sanitized = true;
287 return BroCatliResult::Success;
288 }
289 let mut last_bytes = self.last_bytes[0] as u16 + ((self.last_bytes[1] as u16) << 8);
291 let max = self.last_bytes_len * 8;
292 let mut index = max - 1;
293 for i in 0..max {
294 index = max - 1 - i;
295 if ((1 << index) & last_bytes) != 0 {
296 break; }
298 }
299 if index == 0 {
300 return BroCatliResult::BrotliFileNotCraftedForAppend;
302 }
303 if (last_bytes >> (index - 1)) != 3 {
304 return BroCatliResult::BrotliFileNotCraftedForAppend;
306 }
307 index -= 1; last_bytes &= (1 << index) - 1; self.last_bytes[0] = last_bytes as u8; self.last_bytes[1] = (last_bytes >> 8) as u8;
311 if index >= 8 {
312 if out_bytes.len() > *out_offset {
314 out_bytes[*out_offset] = self.last_bytes[0];
315 self.last_bytes[0] = self.last_bytes[1];
316 *out_offset += 1;
317 self.any_bytes_emitted = true;
318 index -= 8;
319 self.last_bytes_len -= 1;
320 } else {
321 return BroCatliResult::NeedsMoreOutput;
322 }
323 }
324 self.last_byte_bit_offset = index;
325 assert!(index < 8);
326 self.last_byte_sanitized = true;
327 }
328 BroCatliResult::Success
329 }
330
331 fn shift_and_check_new_stream_header(
332 &mut self,
333 mut new_stream_pending: NewStreamData,
334 out_bytes: &mut [u8],
335 out_offset: &mut usize,
336 ) -> BroCatliResult {
337 if new_stream_pending.num_bytes_written.is_none() {
338 let (window_size, window_offset) = if let Ok(results) = parse_window_size(
339 &new_stream_pending.bytes_so_far[..usize::from(new_stream_pending.num_bytes_read)],
340 ) {
341 results
342 } else {
343 return BroCatliResult::InvalidWindowSize;
344 };
345 if self.window_size == 0 {
346 self.window_size = window_size;
348 assert_eq!(self.last_byte_bit_offset, 0); out_bytes[*out_offset] = new_stream_pending.bytes_so_far[0];
350 new_stream_pending.num_bytes_written = Some(1);
351 self.any_bytes_emitted = true;
352 *out_offset += 1;
353 } else {
354 if window_size > self.window_size {
355 return BroCatliResult::WindowSizeLargerThanPreviousFile;
356 }
357 let mut realigned_header: [u8; NUM_STREAM_HEADER_BYTES + 1] =
358 [self.last_bytes[0], 0, 0, 0, 0, 0];
359 let varlen_offset = if let Ok(voffset) = detect_varlen_offset(
360 &new_stream_pending.bytes_so_far
361 [..usize::from(new_stream_pending.num_bytes_read)],
362 ) {
363 voffset
364 } else {
365 return BroCatliResult::BrotliFileNotCraftedForConcatenation;
366 };
367 let mut bytes_so_far = 0u64;
368 for index in 0..usize::from(new_stream_pending.num_bytes_read) {
369 bytes_so_far |=
370 u64::from(new_stream_pending.bytes_so_far[index]) << (index * 8);
371 }
372 bytes_so_far >>= window_offset; bytes_so_far &= (1u64 << (varlen_offset - window_offset)) - 1;
374 let var_len_bytes = (((varlen_offset - window_offset) + 7) / 8);
375 for byte_index in 0..var_len_bytes {
376 let cur_byte = (bytes_so_far >> (byte_index * 8));
377 realigned_header[byte_index] |=
378 ((cur_byte & ((1 << (8 - self.last_byte_bit_offset)) - 1))
379 << self.last_byte_bit_offset) as u8;
380 realigned_header[byte_index + 1] =
381 (cur_byte >> (8 - self.last_byte_bit_offset)) as u8;
382 }
383 let whole_byte_destination =
384 ((usize::from(self.last_byte_bit_offset) + varlen_offset - window_offset) + 7)
385 / 8;
386 let whole_byte_source = (varlen_offset + 7) / 8;
387 if whole_byte_source > usize::from(new_stream_pending.num_bytes_read) {
388 return BroCatliResult::BrotliFileNotCraftedForConcatenation;
389 }
390 let num_whole_bytes_to_copy =
391 usize::from(new_stream_pending.num_bytes_read) - whole_byte_source;
392 for aligned_index in 0..num_whole_bytes_to_copy {
393 realigned_header[whole_byte_destination + aligned_index] =
394 new_stream_pending.bytes_so_far[whole_byte_source + aligned_index];
395 }
396 out_bytes[*out_offset] = realigned_header[0];
397 self.any_bytes_emitted = true;
398 *out_offset += 1;
399 new_stream_pending.num_bytes_read =
401 (whole_byte_destination + num_whole_bytes_to_copy) as u8 - 1;
402 new_stream_pending.num_bytes_written = Some(0);
403 new_stream_pending
404 .bytes_so_far
405 .clone_from_slice(&realigned_header[1..]);
406 }
407 } else {
408 assert_ne!(self.window_size, 0);
409 }
410 let to_copy = min(
411 out_bytes.len() - *out_offset,
412 usize::from(
413 new_stream_pending.num_bytes_read - new_stream_pending.num_bytes_written.unwrap(),
414 ),
415 );
416 out_bytes
417 .split_at_mut(*out_offset)
418 .1
419 .split_at_mut(to_copy)
420 .0
421 .clone_from_slice(
422 new_stream_pending
423 .bytes_so_far
424 .split_at(usize::from(new_stream_pending.num_bytes_written.unwrap()))
425 .1
426 .split_at(to_copy)
427 .0,
428 );
429 *out_offset += to_copy;
430 if to_copy != 0 {
431 self.any_bytes_emitted = true;
432 }
433 new_stream_pending.num_bytes_written =
434 Some((new_stream_pending.num_bytes_written.unwrap() + to_copy as u8));
435 if new_stream_pending.num_bytes_written.unwrap() != new_stream_pending.num_bytes_read {
436 self.new_stream_pending = Some(new_stream_pending);
437 return BroCatliResult::NeedsMoreOutput;
438 }
439 self.new_stream_pending = None;
440 self.last_byte_sanitized = false;
441 self.last_byte_bit_offset = 0;
442 self.last_bytes_len = 0;
443 self.last_bytes = [0, 0];
444 *out_offset -= 1;
446 self.last_bytes[0] = out_bytes[*out_offset];
447 self.last_bytes_len = 1;
448 BroCatliResult::Success
449 }
450 pub fn stream(
451 &mut self,
452 in_bytes: &[u8],
453 in_offset: &mut usize,
454 out_bytes: &mut [u8],
455 out_offset: &mut usize,
456 ) -> BroCatliResult {
457 if let Some(mut new_stream_pending) = self.new_stream_pending {
458 let flush_result = self.flush_previous_stream(out_bytes, out_offset);
459 if let BroCatliResult::Success = flush_result {
460 if usize::from(new_stream_pending.num_bytes_read)
461 < new_stream_pending.bytes_so_far.len()
462 {
463 {
464 let dst = &mut new_stream_pending.bytes_so_far
465 [usize::from(new_stream_pending.num_bytes_read)..];
466 let to_copy = min(dst.len(), in_bytes.len() - *in_offset);
467 dst[..to_copy]
468 .clone_from_slice(in_bytes.split_at(*in_offset).1.split_at(to_copy).0);
469 *in_offset += to_copy;
470 new_stream_pending.num_bytes_read += to_copy as u8;
471 }
472 self.new_stream_pending = Some(new_stream_pending); }
474 if !new_stream_pending.sufficient() {
475 return BroCatliResult::NeedsMoreInput;
476 }
477 if out_bytes.len() == *out_offset {
478 return BroCatliResult::NeedsMoreOutput;
479 }
480 let shift_result = self.shift_and_check_new_stream_header(
481 new_stream_pending,
482 out_bytes,
483 out_offset,
484 );
485 if let BroCatliResult::Success = shift_result {
486 } else {
487 return shift_result;
488 }
489 } else {
490 return flush_result;
491 }
492 if *out_offset == out_bytes.len() {
493 return BroCatliResult::NeedsMoreOutput; }
495 }
496 assert!(self.new_stream_pending.is_none()); if self.last_bytes_len != 2 {
498 if out_bytes.len() == *out_offset {
499 return BroCatliResult::NeedsMoreOutput;
500 }
501 if in_bytes.len() == *in_offset {
502 return BroCatliResult::NeedsMoreInput;
503 }
504 self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset];
505 *in_offset += 1;
506 self.last_bytes_len += 1;
507 if self.last_bytes_len != 2 {
508 if out_bytes.len() == *out_offset {
509 return BroCatliResult::NeedsMoreOutput;
510 }
511 if in_bytes.len() == *in_offset {
512 return BroCatliResult::NeedsMoreInput;
513 }
514 self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset];
515 self.last_bytes_len += 1;
516 *in_offset += 1;
517 }
518 }
519 if out_bytes.len() == *out_offset {
520 return BroCatliResult::NeedsMoreOutput;
521 }
522 if in_bytes.len() == *in_offset {
523 return BroCatliResult::NeedsMoreInput;
524 }
525 let mut to_copy = min(out_bytes.len() - *out_offset, in_bytes.len() - *in_offset);
526 assert_ne!(to_copy, 0);
527 if to_copy == 1 {
528 out_bytes[*out_offset] = self.last_bytes[0];
529 self.last_bytes[0] = self.last_bytes[1];
530 self.last_bytes[1] = in_bytes[*in_offset];
531 *in_offset += 1;
532 *out_offset += 1;
533 if *out_offset == out_bytes.len() {
534 return BroCatliResult::NeedsMoreOutput;
535 }
536 return BroCatliResult::NeedsMoreInput;
537 }
538 out_bytes
539 .split_at_mut(*out_offset)
540 .1
541 .split_at_mut(2)
542 .0
543 .clone_from_slice(&self.last_bytes[..]);
544 *out_offset += 2;
545 let (new_in_offset, last_two) = in_bytes
546 .split_at(*in_offset)
547 .1
548 .split_at(to_copy)
549 .0
550 .split_at(to_copy - 2);
551 self.last_bytes.clone_from_slice(last_two);
552 *in_offset += 2; to_copy -= 2;
554 out_bytes
555 .split_at_mut(*out_offset)
556 .1
557 .split_at_mut(to_copy)
558 .0
559 .clone_from_slice(new_in_offset);
560 *out_offset += to_copy;
561 *in_offset += to_copy;
562 if *out_offset == out_bytes.len() {
563 return BroCatliResult::NeedsMoreOutput;
564 }
565 BroCatliResult::NeedsMoreInput
566 }
567 fn append_eof_metablock_to_last_bytes(&mut self) {
568 assert!(self.last_byte_sanitized);
569 let mut last_bytes = self.last_bytes[0] as u16 | ((self.last_bytes[1] as u16) << 8);
570 let bit_end = (self.last_bytes_len - 1) * 8 + self.last_byte_bit_offset;
571 last_bytes |= 3 << bit_end;
572 self.last_bytes[0] = last_bytes as u8;
573 self.last_bytes[1] = (last_bytes >> 8) as u8;
574 self.last_byte_sanitized = false;
575 self.last_byte_bit_offset += 2;
576 if self.last_byte_bit_offset >= 8 {
577 self.last_byte_bit_offset -= 8;
578 self.last_bytes_len += 1;
579 }
580 }
581 pub fn finish(&mut self, out_bytes: &mut [u8], out_offset: &mut usize) -> BroCatliResult {
582 if self.last_byte_sanitized && self.last_bytes_len != 0 {
583 self.append_eof_metablock_to_last_bytes();
584 }
585 while self.last_bytes_len != 0 {
586 if *out_offset == out_bytes.len() {
587 return BroCatliResult::NeedsMoreOutput;
588 }
589 out_bytes[*out_offset] = self.last_bytes[0];
590 *out_offset += 1;
591 self.last_bytes_len -= 1;
592 self.last_bytes[0] = self.last_bytes[1];
593 self.any_bytes_emitted = true;
594 }
595 if !self.any_bytes_emitted {
596 if out_bytes.len() == *out_offset {
597 return BroCatliResult::NeedsMoreOutput;
598 }
599 self.any_bytes_emitted = true;
600 out_bytes[*out_offset] = b';';
601 *out_offset += 1;
602 }
603 BroCatliResult::Success
604 }
605}
606
607#[cfg(test)]
608mod test {
609 use super::BroCatli;
610
611 fn make_valid_serialized_buffer(buffer: &mut [u8]) {
612 buffer[8] = 2;
613 buffer[9] = (1 << 6) | (1 << 7);
614 buffer[10] = 7;
615 buffer[11] = 22;
616 buffer[12] = super::NUM_STREAM_HEADER_BYTES as u8;
617 buffer[13] = 3;
618 }
619
620 #[test]
621 fn test_deserialization() {
622 let broccoli = BroCatli {
623 new_stream_pending: Some(super::NewStreamData {
624 bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES],
625 num_bytes_read: super::NUM_STREAM_HEADER_BYTES as u8,
626 num_bytes_written: Some(3),
627 }),
628 last_bytes: [0x45, 0x46],
629 last_bytes_len: 1,
630 last_byte_sanitized: true,
631 any_bytes_emitted: false,
632 last_byte_bit_offset: 7,
633 window_size: 22,
634 };
635 let mut buffer = [0u8; 248];
636 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
637 let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap();
638 assert_eq!(broccoli.last_bytes, bc.last_bytes);
639 assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len);
640 assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized);
641 assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset);
642 assert_eq!(broccoli.window_size, bc.window_size);
643 assert_eq!(
644 broccoli.new_stream_pending.unwrap().bytes_so_far,
645 bc.new_stream_pending.unwrap().bytes_so_far
646 );
647 assert_eq!(
648 broccoli.new_stream_pending.unwrap().num_bytes_read,
649 bc.new_stream_pending.unwrap().num_bytes_read
650 );
651 assert_eq!(
652 broccoli.new_stream_pending.unwrap().num_bytes_written,
653 bc.new_stream_pending.unwrap().num_bytes_written
654 );
655 }
656 #[test]
657 fn test_deserialization_any_written() {
658 let broccoli = BroCatli {
659 new_stream_pending: Some(super::NewStreamData {
660 bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES],
661 num_bytes_read: super::NUM_STREAM_HEADER_BYTES as u8,
662 num_bytes_written: Some(3),
663 }),
664 last_bytes: [0x45, 0x46],
665 last_bytes_len: 1,
666 last_byte_sanitized: true,
667 any_bytes_emitted: true,
668 last_byte_bit_offset: 7,
669 window_size: 22,
670 };
671 let mut buffer = [0u8; 248];
672 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
673 let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap();
674 assert_eq!(broccoli.last_bytes, bc.last_bytes);
675 assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len);
676 assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized);
677 assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset);
678 assert_eq!(broccoli.window_size, bc.window_size);
679 assert_eq!(
680 broccoli.new_stream_pending.unwrap().bytes_so_far,
681 bc.new_stream_pending.unwrap().bytes_so_far
682 );
683 assert_eq!(
684 broccoli.new_stream_pending.unwrap().num_bytes_read,
685 bc.new_stream_pending.unwrap().num_bytes_read
686 );
687 assert_eq!(
688 broccoli.new_stream_pending.unwrap().num_bytes_written,
689 bc.new_stream_pending.unwrap().num_bytes_written
690 );
691 }
692 #[test]
693 fn test_serialization() {
694 let mut buffer = [0u8; 248];
695 let mut broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
696 let mut buffer2 = [0u8; 248];
697 broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
698 assert_eq!(&buffer[..], &buffer2[..]);
699 for (index, item) in buffer.iter_mut().enumerate() {
700 *item = index as u8;
701 }
702 make_valid_serialized_buffer(&mut buffer[..]);
703 broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
704 broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
705 broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap();
706 for (_index, item) in buffer.iter_mut().enumerate() {
707 *item = 0;
708 }
709 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
710 assert_eq!(&buffer[..], &buffer2[..]);
711 for (index, item) in buffer.iter_mut().enumerate() {
712 *item = 0xff ^ index as u8;
713 }
714 make_valid_serialized_buffer(&mut buffer[..]);
715 broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
716 broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
717 broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap();
718 for (_index, item) in buffer.iter_mut().enumerate() {
719 *item = 0;
720 }
721 broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
722 assert_eq!(&buffer[..], &buffer2[..]);
723 }
724 #[test]
725 fn test_deserialization_rejects_invalid_state_fields() {
726 let mut buffer = [0u8; 248];
727 make_valid_serialized_buffer(&mut buffer[..]);
728
729 let mut invalid = buffer;
730 invalid[8] = 3;
731 assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
732
733 invalid = buffer;
734 invalid[10] = 8;
735 assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
736
737 invalid = buffer;
738 invalid[11] = 9;
739 assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
740
741 invalid = buffer;
742 invalid[12] = super::NUM_STREAM_HEADER_BYTES as u8 + 1;
743 assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
744
745 invalid = buffer;
746 invalid[13] = buffer[12] + 1;
747 assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
748 }
749 #[test]
750 fn test_cat_empty_stream() {
751 let empty_catable = [b';'];
752 let mut bcat = super::BroCatli::default();
753 let mut in_offset = 0usize;
754 let mut out_bytes = [0u8; 32];
755 let mut out_offset = 0usize;
756 bcat.new_brotli_file();
757 let mut res = bcat.stream(
758 &empty_catable[..],
759 &mut in_offset,
760 &mut out_bytes[..],
761 &mut out_offset,
762 );
763 assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
764 bcat.new_brotli_file();
765 in_offset = 0;
766 res = bcat.stream(
767 &empty_catable[..],
768 &mut in_offset,
769 &mut out_bytes[..],
770 &mut out_offset,
771 );
772 assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
773 res = bcat.finish(&mut out_bytes[..], &mut out_offset);
774 assert_eq!(res, super::BroCatliResult::Success);
775 assert_ne!(out_offset, 0);
776 assert_eq!(&out_bytes[..out_offset], &[b';']);
777 }
778 #[test]
779 fn test_cat_truncated_metadata_header_fails() {
780 let empty_catable = [b';'];
781 let mut bcat = super::BroCatli::new_with_window_size(22);
782 let mut in_offset = 0usize;
783 let mut out_bytes = [0u8; 32];
784 let mut out_offset = 0usize;
785 let mut res = bcat.stream(
786 &empty_catable[..],
787 &mut in_offset,
788 &mut out_bytes[..],
789 &mut out_offset,
790 );
791 assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
792
793 let truncated_metadata = [0x71, 0x1b, 0, 0];
794 bcat.new_brotli_file();
795 in_offset = 0;
796 out_offset = 0;
797 res = bcat.stream(
798 &truncated_metadata[..],
799 &mut in_offset,
800 &mut out_bytes[..],
801 &mut out_offset,
802 );
803 assert_eq!(
804 res,
805 super::BroCatliResult::BrotliFileNotCraftedForConcatenation
806 );
807 }
808 #[test]
809 fn test_try_new_with_window_size_invalid_returns_error() {
810 use super::BroCatliResult;
811 for ws in 0u8..=9 {
813 match BroCatli::try_new_with_window_size(ws) {
814 Err(BroCatliResult::InvalidWindowSize) => {}
815 Err(_) => panic!("window_size {} returned wrong error variant", ws),
816 Ok(_) => panic!("window_size {} should be rejected", ws),
817 }
818 #[cfg(feature = "std")]
819 assert!(
820 std::panic::catch_unwind(|| BroCatli::new_with_window_size(ws)).is_err(),
821 "window_size {} should panic through the legacy constructor",
822 ws
823 );
824 }
825 }
826 #[test]
827 fn test_new_with_window_size_valid() {
828 for ws in 10u8..=30 {
830 let _ = BroCatli::new_with_window_size(ws);
831 assert!(
832 BroCatli::try_new_with_window_size(ws).is_ok(),
833 "window_size {} should be accepted",
834 ws
835 );
836 }
837 }
838}