use crate::bitreader::BitReader;
use crate::block;
use crate::block_scan;
use crate::{BLOCK_MAGIC, FINAL_MAGIC};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DecodeMode {
Serial,
Interleaved,
}
pub struct ChunkSplit {
pub segment_starts: Vec<block_scan::BlockBoundary>,
pub decode_segments: usize,
pub consumed: usize,
}
pub fn split_chunk(
data: &[u8],
n_segments: usize,
max_blocksize: u32,
is_last: bool,
) -> Option<ChunkSplit> {
let first_block = block_scan::find_next_block(data, 0)?;
let splits = block_scan::split_boundaries_parallel(data, n_segments, max_blocksize);
let mut segment_starts = Vec::with_capacity(n_segments);
segment_starts.push(first_block);
for s in &splits {
if segment_starts.last().map_or(true, |prev: &block_scan::BlockBoundary| {
prev.bit_offset != s.bit_offset
}) {
segment_starts.push(*s);
}
}
let n = segment_starts.len();
let decode_segments = if is_last {
n
} else if n > 1 {
n - 1
} else {
return None;
};
let consumed = if decode_segments < n {
segment_starts[decode_segments].byte_offset()
} else {
data.len()
};
Some(ChunkSplit { segment_starts, decode_segments, consumed })
}
pub struct SegmentDecoded {
pub data: Vec<u8>,
pub block_crcs: Vec<u32>,
}
pub fn decode_segment(
data: &[u8],
start_bit: u64,
end_bit: u64,
max_blocksize: u32,
) -> Vec<u8> {
let mut output = Vec::with_capacity(seg_cap_hint(start_bit, end_bit));
let mut block_crcs: Vec<u32> = Vec::new();
decode_segment_inner(data, start_bit, end_bit, max_blocksize, &mut output, &mut block_crcs, DecodeMode::Serial);
output
}
pub fn decode_segment_checked(
data: &[u8],
start_bit: u64,
end_bit: u64,
max_blocksize: u32,
) -> Result<SegmentDecoded, block::BlockError> {
let mut output = Vec::with_capacity(seg_cap_hint(start_bit, end_bit));
let mut block_crcs: Vec<u32> = Vec::new();
match decode_segment_inner(data, start_bit, end_bit, max_blocksize, &mut output, &mut block_crcs, DecodeMode::Serial) {
None => Ok(SegmentDecoded { data: output, block_crcs }),
Some(e) => Err(e),
}
}
#[inline]
fn seg_cap_hint(start_bit: u64, end_bit: u64) -> usize {
let comp_span = (end_bit.saturating_sub(start_bit) / 8) as usize;
comp_span.saturating_mul(4).min(256 * 1024 * 1024)
}
fn decode_segment_inner(
data: &[u8],
start_bit: u64,
end_bit: u64,
max_blocksize: u32,
output: &mut Vec<u8>,
block_crcs: &mut Vec<u32>,
mode: DecodeMode,
) -> Option<block::BlockError> {
match mode {
DecodeMode::Interleaved => {
decode_segment_interleaved(data, start_bit, end_bit, max_blocksize, output, block_crcs)
}
DecodeMode::Serial => {
decode_segment_serial(data, start_bit, end_bit, max_blocksize, output, block_crcs)
}
}
}
fn decode_segment_serial(
data: &[u8],
start_bit: u64,
end_bit: u64,
max_blocksize: u32,
output: &mut Vec<u8>,
block_crcs: &mut Vec<u32>,
) -> Option<block::BlockError> {
let total_bits = data.len() as u64 * 8;
let mut reader = BitReader::from_bit_offset(data, (start_bit + 48) as usize);
match decode_block_into_vec(output, &mut reader, max_blocksize) {
Ok(crc) => block_crcs.push(crc),
Err(e) => return Some(e),
}
loop {
let pos = reader.position() as u64;
if pos + 48 > total_bits || pos >= end_bit {
break;
}
let magic = match reader.read_u64(48) {
Some(v) => v,
None => break,
};
if magic == BLOCK_MAGIC {
match decode_block_into_vec(output, &mut reader, max_blocksize) {
Ok(crc) => block_crcs.push(crc),
Err(e) => return Some(e),
}
} else if magic == FINAL_MAGIC {
if reader.read_u32(32).is_none() { break; }
let p = reader.position();
let pad = (8 - (p % 8)) % 8;
if pad > 0 { BitReader::skip(&mut reader, pad); }
match reader.read_u32(32) {
Some(h) => {
let b = h.to_be_bytes();
if &b[..3] != b"BZh" {
break;
}
}
None => break,
}
} else {
break;
}
}
None
}
const MLP_N: usize = 4;
fn decode_segment_interleaved(
data: &[u8],
start_bit: u64,
end_bit: u64,
max_blocksize: u32,
output: &mut Vec<u8>,
block_crcs: &mut Vec<u32>,
) -> Option<block::BlockError> {
let total_bits = data.len() as u64 * 8;
let mut reader = BitReader::from_bit_offset(data, (start_bit + 48) as usize);
let mut first = true;
let mut cores: Vec<block::BlockCore> = Vec::with_capacity(MLP_N);
loop {
cores.clear();
let mut ended = false;
let mut gather_err: Option<block::BlockError> = None;
while cores.len() < MLP_N {
match next_block_core(&mut reader, end_bit, total_bits, max_blocksize, &mut first) {
Ok(Some(core)) => cores.push(core),
Ok(None) => { ended = true; break; }
Err(e) => { gather_err = Some(e); break; }
}
}
if !cores.is_empty() {
if let Err(e) = block::decode_blocks_interleaved(&mut cores, output, block_crcs) {
return Some(e);
}
}
if let Some(e) = gather_err {
return Some(e);
}
if ended {
break;
}
}
None
}
#[inline]
fn next_block_core(
reader: &mut BitReader<'_>,
end_bit: u64,
total_bits: u64,
max_blocksize: u32,
first: &mut bool,
) -> Result<Option<block::BlockCore>, block::BlockError> {
if *first {
*first = false;
return block::decode_block_core(reader, max_blocksize).map(Some);
}
loop {
let pos = BitReader::position(reader) as u64;
if pos + 48 > total_bits || pos >= end_bit {
return Ok(None);
}
let magic = match reader.read_u64(48) {
Some(v) => v,
None => return Ok(None),
};
if magic == BLOCK_MAGIC {
return block::decode_block_core(reader, max_blocksize).map(Some);
} else if magic == FINAL_MAGIC {
if reader.read_u32(32).is_none() {
return Ok(None);
}
let p = BitReader::position(reader);
let pad = (8 - (p % 8)) % 8;
if pad > 0 {
BitReader::skip(reader, pad);
}
match reader.read_u32(32) {
Some(h) => {
let b = h.to_be_bytes();
if &b[..3] != b"BZh" {
return Ok(None);
}
}
None => return Ok(None),
}
} else {
return Ok(None);
}
}
}
thread_local! {
static CRC_SCRATCH: std::cell::RefCell<Vec<u32>> = const { std::cell::RefCell::new(Vec::new()) };
}
pub fn decode_segment_checked_into(
data: &[u8],
start_bit: u64,
end_bit: u64,
max_blocksize: u32,
out: &mut Vec<u8>,
mode: DecodeMode,
) -> Result<(), block::BlockError> {
out.clear();
out.reserve(seg_cap_hint(start_bit, end_bit));
CRC_SCRATCH.with(|c| {
let mut crcs = c.borrow_mut();
crcs.clear();
match decode_segment_inner(data, start_bit, end_bit, max_blocksize, out, &mut crcs, mode) {
None => Ok(()),
Some(e) => Err(e),
}
})
}
#[inline]
fn decode_block_into_vec(
output: &mut Vec<u8>,
reader: &mut BitReader<'_>,
max_blocksize: u32,
) -> Result<u32, block::BlockError> {
let (_written, crc) = block::decode_block_into(reader, max_blocksize, output)?;
Ok(crc)
}
#[cfg(test)]
mod tests {
use super::*;
fn max_blocksize(data: &[u8]) -> u32 {
100_000 * (data[3] - b'0') as u32
}
#[test]
fn chunk_hello() {
let data = include_bytes!("../test_data/hello.bz2");
let split = split_chunk(data, 4, max_blocksize(data), true).unwrap();
let total_bits = data.len() as u64 * 8;
let mut output = Vec::new();
for i in 0..split.decode_segments {
let start = split.segment_starts[i].bit_offset;
let end = if i + 1 < split.segment_starts.len() {
split.segment_starts[i + 1].bit_offset
} else {
total_bits
};
output.extend_from_slice(&decode_segment(data, start, end, max_blocksize(data)));
}
assert_eq!(&output, b"Hello, World!\n");
}
fn decode_whole(data: &[u8], mode: DecodeMode) -> Vec<u8> {
let split = split_chunk(data, 1, max_blocksize(data), true).unwrap();
let total_bits = data.len() as u64 * 8;
let mut out_all = Vec::new();
let mut buf = Vec::new();
for i in 0..split.decode_segments {
let start = split.segment_starts[i].bit_offset;
let end = if i + 1 < split.segment_starts.len() {
split.segment_starts[i + 1].bit_offset
} else {
total_bits
};
decode_segment_checked_into(data, start, end, max_blocksize(data), &mut buf, mode)
.unwrap();
out_all.extend_from_slice(&buf);
}
out_all
}
#[test]
fn interleaved_matches_serial() {
let data = include_bytes!("../test_data/liechtenstein.osm.bz2");
let reference = crate::stream::decompress(data).unwrap();
let serial = decode_whole(data, DecodeMode::Serial);
let interleaved = decode_whole(data, DecodeMode::Interleaved);
assert_eq!(serial, reference, "serial mode diverged from reference");
assert_eq!(interleaved.len(), reference.len(), "interleaved size mismatch");
assert_eq!(interleaved, reference, "interleaved mode diverged from reference");
}
const REP_4M_A_BZ2: [u8; 49] = [
0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0xf7, 0x09, 0x6c, 0x45, 0x00, 0x20,
0x22, 0x0c, 0x00, 0x80, 0x04, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x30, 0xcc, 0x05, 0x49, 0xea,
0x71, 0x01, 0x80, 0x50, 0x06, 0x01, 0xe2, 0xee, 0x48, 0xa7, 0x0a, 0x12, 0x1e, 0xe1, 0x2d, 0x88,
0xa0,
];
#[test]
fn rle1_expansion_far_exceeds_blocksize() {
let data = &REP_4M_A_BZ2;
let expected = vec![b'A'; 4 * 1024 * 1024];
assert_eq!(crate::stream::decompress(data).unwrap(), expected);
assert_eq!(crate::parallel::decompress_parallel(data).unwrap(), expected);
let split = split_chunk(data, 4, max_blocksize(data), true).unwrap();
let total_bits = data.len() as u64 * 8;
let mut out = Vec::new();
for i in 0..split.decode_segments {
let start = split.segment_starts[i].bit_offset;
let end = if i + 1 < split.segment_starts.len() {
split.segment_starts[i + 1].bit_offset
} else {
total_bits
};
let seg = decode_segment_checked(data, start, end, max_blocksize(data)).unwrap();
out.extend_from_slice(&seg.data);
}
assert_eq!(out, expected);
assert_eq!(decode_whole(data, DecodeMode::Interleaved), expected);
}
#[test]
fn chunk_liechtenstein() {
let data = include_bytes!("../test_data/liechtenstein.osm.bz2");
let n = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(4);
let split = split_chunk(data, n, max_blocksize(data), true).unwrap();
let total_bits = data.len() as u64 * 8;
let mut output = Vec::new();
for i in 0..split.decode_segments {
let start = split.segment_starts[i].bit_offset;
let end = if i + 1 < split.segment_starts.len() {
split.segment_starts[i + 1].bit_offset
} else {
total_bits
};
output.extend_from_slice(&decode_segment(data, start, end, max_blocksize(data)));
}
let reference = crate::stream::decompress(data).unwrap();
assert_eq!(output.len(), reference.len());
assert_eq!(output, reference);
}
#[test]
fn chunk_split_simulation() {
let data = include_bytes!("../test_data/liechtenstein.osm.bz2");
let mbs = max_blocksize(data);
let mid = data.len() / 2;
let n = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(4);
let split1 = split_chunk(&data[..mid], n, mbs, false).unwrap();
let consumed1 = split1.consumed;
assert!(consumed1 <= mid);
let total_bits1 = mid as u64 * 8;
let mut out1 = Vec::new();
for i in 0..split1.decode_segments {
let start = split1.segment_starts[i].bit_offset;
let end = if i + 1 < split1.segment_starts.len() {
split1.segment_starts[i + 1].bit_offset
} else {
total_bits1
};
out1.extend_from_slice(&decode_segment(&data[..mid], start, end, mbs));
}
assert!(!out1.is_empty());
let chunk2 = &data[consumed1..];
let split2 = split_chunk(chunk2, n, mbs, true).unwrap();
let total_bits2 = chunk2.len() as u64 * 8;
let mut out2 = Vec::new();
for i in 0..split2.decode_segments {
let start = split2.segment_starts[i].bit_offset;
let end = if i + 1 < split2.segment_starts.len() {
split2.segment_starts[i + 1].bit_offset
} else {
total_bits2
};
out2.extend_from_slice(&decode_segment(chunk2, start, end, mbs));
}
let mut combined = out1;
combined.extend_from_slice(&out2);
let reference = crate::stream::decompress(data).unwrap();
assert_eq!(combined.len(), reference.len());
assert_eq!(combined, reference);
}
}