brotli/concat/
mod.rs

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    // 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
73fn 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)]
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    // need to make sure that window sizes stay similar or get smaller
132    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            // 14, 15 reserved
198            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 {
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)]
579mod 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}