extern crate brotli_decompressor;
#[macro_use]
extern crate alloc_no_stdlib;
use alloc_no_stdlib::{AllocatedStackMemory, Allocator, SliceWrapper, SliceWrapperMut,
StackAllocator, bzero};
use brotli_decompressor::{brotli_decode_prealloc, BrotliDecompressStream, BrotliResult,
BrotliState, HuffmanCode};
use std::ops;
declare_stack_allocator_struct!(MemPool, 512, stack);
const RING_BUFFER_SLACK: usize = 542 + 24;
const WINDOW: usize = 1 << 16;
const X_COMPRESSED: [u8; 5] = [0x0b, 0x00, 0x80, 0x58, 0x03];
struct BitWriter {
bytes: Vec<u8>,
bits: usize,
}
impl BitWriter {
fn put(&mut self, count: usize, value: u32) {
for i in 0..count {
if self.bits % 8 == 0 {
self.bytes.push(0);
}
let last = self.bytes.len() - 1;
self.bytes[last] |= (((value >> i) & 1) as u8) << (self.bits % 8);
self.bits += 1;
}
}
fn raw(&mut self, data: &[u8]) {
self.bytes.extend_from_slice(data);
self.bits = self.bytes.len() * 8;
}
}
fn uncompressed_stream(lengths: &[usize]) -> (Vec<u8>, Vec<u8>) {
let mut writer = BitWriter { bytes: Vec::new(), bits: 0 };
writer.put(1, 0); let mut data = Vec::new();
let mut seed = 1u32;
for &length in lengths {
let block: Vec<u8> = (0..length).map(|_| {
seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
(seed >> 16) as u8
}).collect();
writer.put(1, 0);
writer.put(2, 0);
writer.put(16, length as u32 - 1);
writer.put(1, 1);
writer.raw(&block);
data.extend_from_slice(&block);
}
writer.put(2, 3); (writer.bytes, data)
}
fn growing_stream() -> (Vec<u8>, Vec<u8>) {
uncompressed_stream(&[1000, 3000, 10000, 30000])
}
fn decode_prealloc(input: &[u8], output_len: usize, scratch_u8_len: usize) -> Vec<u8> {
let mut output = vec![0u8; output_len];
let mut scratch_u8 = vec![0u8; scratch_u8_len];
let mut scratch_u32 = vec![0u32; 4 << 10];
let mut scratch_hc = vec![HuffmanCode::default(); 20 << 10];
let info = brotli_decode_prealloc(input, &mut output, &mut scratch_u8, &mut scratch_u32,
&mut scratch_hc);
assert!(matches!(info.result, BrotliResult::ResultSuccess), "decoding failed");
output.truncate(info.decoded_size);
output
}
#[test]
fn prealloc_pool_that_holds_the_window_decodes_a_growing_stream() {
let (input, expected) = growing_stream();
let output = decode_prealloc(&input, expected.len(), WINDOW + RING_BUFFER_SLACK);
assert!(output == expected);
}
#[test]
fn prealloc_tiny_pool_decodes_a_tiny_stream() {
let output = decode_prealloc(&X_COMPRESSED, 1, 32 + RING_BUFFER_SLACK);
assert_eq!(&output[..], b"X");
}
#[test]
fn caller_pool_decodes_a_tiny_last_metablock_as_before() {
let input = [0xa1, 0x80, 0x00, 0x00, 0x20, 0x01, 0x52, 0x83, 0x20, 0x53, 0x87, 0xe7, 0xf4,
0x00];
let mut u8_pool = vec![0u8; 64 + RING_BUFFER_SLACK + 1 + 64 + 4];
let mut u32_pool = vec![0u32; 4 << 10];
let mut hc_pool = vec![HuffmanCode::default(); 20 << 10];
let mut state = BrotliState::new(MemPool::<u8>::new_allocator(&mut u8_pool, bzero),
MemPool::<u32>::new_allocator(&mut u32_pool, bzero),
MemPool::<HuffmanCode>::new_allocator(&mut hc_pool, bzero));
let mut output = [0u8; 32];
let mut available_in = input.len();
let mut input_offset = 0;
let mut available_out = output.len();
let mut output_offset = 0;
let mut total_out = 0;
let result = BrotliDecompressStream(&mut available_in, &mut input_offset, &input,
&mut available_out, &mut output_offset, &mut output,
&mut total_out, &mut state);
assert!(matches!(result, BrotliResult::ResultSuccess));
assert_eq!(&output[..output_offset], b"hello hello hello");
}
fn decode_with_caller_pool(input: &[u8], output_len: usize, u8_pool_len: usize,
initial_ring_buffer_size: u32) -> Option<Vec<u8>> {
let mut u8_pool = vec![0u8; u8_pool_len];
let mut u32_pool = vec![0u32; 4 << 10];
let mut hc_pool = vec![HuffmanCode::default(); 20 << 10];
let mut state = BrotliState::new(MemPool::<u8>::new_allocator(&mut u8_pool, bzero),
MemPool::<u32>::new_allocator(&mut u32_pool, bzero),
MemPool::<HuffmanCode>::new_allocator(&mut hc_pool, bzero));
assert!(state.set_initial_ring_buffer_size(initial_ring_buffer_size));
let mut output = vec![0u8; output_len];
let mut available_in = input.len();
let mut input_offset = 0;
let mut available_out = output.len();
let mut output_offset = 0;
let mut total_out = 0;
match BrotliDecompressStream(&mut available_in, &mut input_offset, input,
&mut available_out, &mut output_offset, &mut output,
&mut total_out, &mut state) {
BrotliResult::ResultSuccess => {
output.truncate(output_offset);
Some(output)
}
_ => None,
}
}
#[test]
fn caller_pool_that_holds_the_window_decodes_a_growing_stream_from_the_whole_window() {
let (input, expected) = growing_stream();
let pool_len = WINDOW + RING_BUFFER_SLACK;
assert!(decode_with_caller_pool(&input, expected.len(), pool_len, u32::MAX).as_ref() ==
Some(&expected));
}
#[test]
#[should_panic(expected = "OOM")]
fn caller_pool_that_holds_the_window_runs_out_growing_from_the_default_size() {
let (input, expected) = growing_stream();
let _ = decode_with_caller_pool(&input, expected.len(), WINDOW + RING_BUFFER_SLACK, 0);
}
#[test]
fn caller_pool_decodes_a_tiny_uncompressed_stream_as_before() {
for &initial_ring_buffer_size in &[0, u32::MAX] {
let output = decode_with_caller_pool(&X_COMPRESSED, 1, 32 + RING_BUFFER_SLACK,
initial_ring_buffer_size);
assert!(output.as_ref().map(|output| &output[..]) == Some(&b"X"[..]));
}
}
#[cfg(not(feature = "std"))]
#[test]
fn no_std_decode_needs_no_bigger_buffer() {
fn decode(input: &[u8], buffer_len: usize) -> Vec<u8> {
let mut buffer = vec![0u8; buffer_len];
let info = brotli_decompressor::brotli_decode(input, &mut buffer);
assert!(matches!(info.result, BrotliResult::ResultSuccess), "decoding failed");
buffer.truncate(info.decoded_size);
buffer
}
std::thread::Builder::new().stack_size(16 << 20).spawn(|| {
let (input, expected) = growing_stream();
assert!(decode(&input, 115 << 10) == expected);
assert_eq!(&decode(&X_COMPRESSED, 2048)[..], b"X");
}).unwrap().join().unwrap();
}