Coverage Report

Created: 2025-07-18 07:03

/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
}