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