use crate::constants::*;
use crate::error::{Error, Result};
use crate::varint::decode_varint;
pub struct Decoder {
#[allow(dead_code)]
allow_snappy: bool,
}
impl Decoder {
pub fn new() -> Self {
Decoder { allow_snappy: true }
}
pub fn new_s2_only() -> Self {
Decoder {
allow_snappy: false,
}
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
pub fn decode(src: &[u8]) -> Result<Vec<u8>> {
let (dlen, header_len) = decode_len(src)?;
let mut dst = vec![0u8; dlen];
s2_decode(&mut dst, &src[header_len..])?;
Ok(dst)
}
pub fn decode_snappy(src: &[u8]) -> Result<Vec<u8>> {
decode(src)
}
#[allow(dead_code)]
pub fn decode_into(dst: &mut [u8], src: &[u8]) -> Result<usize> {
let (dlen, header_len) = decode_len(src)?;
if dst.len() < dlen {
return Err(Error::BufferTooSmall);
}
s2_decode(&mut dst[..dlen], &src[header_len..])?;
Ok(dlen)
}
pub fn decode_len(src: &[u8]) -> Result<(usize, usize)> {
let (v, n) = decode_varint(src)?;
if v > 0xffffffff {
return Err(Error::Corrupt);
}
#[cfg(target_pointer_width = "32")]
{
if v > 0x7fffffff {
return Err(Error::TooLarge);
}
}
Ok((v as usize, n))
}
fn s2_decode(dst: &mut [u8], src: &[u8]) -> Result<()> {
let mut d = 0; let mut s = 0; let mut offset = 0;
while s < src.len().saturating_sub(5) {
let tag = src[s] & 0x03;
match tag {
TAG_LITERAL => {
let (length, bytes_consumed) = decode_literal_length(&src[s..])?;
s += bytes_consumed;
if length > dst.len() - d || length > src.len() - s {
return Err(Error::Corrupt);
}
dst[d..d + length].copy_from_slice(&src[s..s + length]);
d += length;
s += length;
}
TAG_COPY1 => {
let (new_offset, length, bytes_consumed) = decode_copy1(&src[s..], offset)?;
s += bytes_consumed;
offset = new_offset;
if offset == 0 || d < offset || length > dst.len() - d {
return Err(Error::Corrupt);
}
copy_within(dst, d, offset, length);
d += length;
}
TAG_COPY2 => {
if s + 3 > src.len() {
return Err(Error::Corrupt);
}
offset = u16::from_le_bytes([src[s + 1], src[s + 2]]) as usize;
let length = 1 + ((src[s] >> 2) as usize);
s += 3;
if offset == 0 || d < offset || length > dst.len() - d {
return Err(Error::Corrupt);
}
copy_within(dst, d, offset, length);
d += length;
}
TAG_COPY4 => {
if s + 5 > src.len() {
return Err(Error::Corrupt);
}
offset =
u32::from_le_bytes([src[s + 1], src[s + 2], src[s + 3], src[s + 4]]) as usize;
let length = 1 + ((src[s] >> 2) as usize);
s += 5;
if offset == 0 || d < offset || length > dst.len() - d {
return Err(Error::Corrupt);
}
copy_within(dst, d, offset, length);
d += length;
}
_ => unreachable!(),
}
}
while s < src.len() {
let tag = src[s] & 0x03;
match tag {
TAG_LITERAL => {
let (length, bytes_consumed) = decode_literal_length(&src[s..])?;
s += bytes_consumed;
if s > src.len() || length > dst.len() - d || length > src.len() - s {
return Err(Error::Corrupt);
}
dst[d..d + length].copy_from_slice(&src[s..s + length]);
d += length;
s += length;
}
TAG_COPY1 => {
let (new_offset, length, bytes_consumed) = decode_copy1(&src[s..], offset)?;
s += bytes_consumed;
if s > src.len() {
return Err(Error::Corrupt);
}
offset = new_offset;
if offset == 0 || d < offset || length > dst.len() - d {
return Err(Error::Corrupt);
}
copy_within(dst, d, offset, length);
d += length;
}
TAG_COPY2 => {
s += 3;
if s > src.len() {
return Err(Error::Corrupt);
}
offset = u16::from_le_bytes([src[s - 2], src[s - 1]]) as usize;
let length = 1 + ((src[s - 3] >> 2) as usize);
if offset == 0 || d < offset || length > dst.len() - d {
return Err(Error::Corrupt);
}
copy_within(dst, d, offset, length);
d += length;
}
TAG_COPY4 => {
s += 5;
if s > src.len() {
return Err(Error::Corrupt);
}
offset =
u32::from_le_bytes([src[s - 4], src[s - 3], src[s - 2], src[s - 1]]) as usize;
let length = 1 + ((src[s - 5] >> 2) as usize);
if offset == 0 || d < offset || length > dst.len() - d {
return Err(Error::Corrupt);
}
copy_within(dst, d, offset, length);
d += length;
}
_ => unreachable!(),
}
}
if d != dst.len() {
return Err(Error::Corrupt);
}
Ok(())
}
fn decode_literal_length(src: &[u8]) -> Result<(usize, usize)> {
let x = (src[0] >> 2) as u32;
match x {
0..=59 => Ok((x as usize + 1, 1)),
60 => {
if src.len() < 2 {
return Err(Error::Corrupt);
}
Ok((src[1] as usize + 1, 2))
}
61 => {
if src.len() < 3 {
return Err(Error::Corrupt);
}
let len = u16::from_le_bytes([src[1], src[2]]) as usize;
Ok((len + 1, 3))
}
62 => {
if src.len() < 4 {
return Err(Error::Corrupt);
}
let len = u32::from_le_bytes([src[1], src[2], src[3], 0]) as usize;
Ok((len + 1, 4))
}
63 => {
if src.len() < 5 {
return Err(Error::Corrupt);
}
let len = u32::from_le_bytes([src[1], src[2], src[3], src[4]]) as usize;
Ok((len + 1, 5))
}
_ => Err(Error::Corrupt),
}
}
fn decode_copy1(src: &[u8], last_offset: usize) -> Result<(usize, usize, usize)> {
if src.len() < 2 {
return Err(Error::Corrupt);
}
let toffset = ((src[0] as usize & 0xe0) << 3) | (src[1] as usize);
let mut length = ((src[0] >> 2) & 0x7) as usize;
if toffset == 0 {
match length {
5 => {
if src.len() < 3 {
return Err(Error::Corrupt);
}
length = src[2] as usize + 4;
Ok((last_offset, length + 4, 3))
}
6 => {
if src.len() < 4 {
return Err(Error::Corrupt);
}
length = u16::from_le_bytes([src[2], src[3]]) as usize + (1 << 8);
Ok((last_offset, length + 4, 4))
}
7 => {
if src.len() < 5 {
return Err(Error::Corrupt);
}
length = u32::from_le_bytes([src[2], src[3], src[4], 0]) as usize + (1 << 16);
Ok((last_offset, length + 4, 5))
}
_ => {
Ok((last_offset, length + 4, 2))
}
}
} else {
Ok((toffset, length + 4, 2))
}
}
#[inline]
fn copy_within(dst: &mut [u8], d: usize, offset: usize, length: usize) {
let src_start = d - offset;
if offset >= length {
dst.copy_within(src_start..src_start + length, d);
} else {
for i in 0..length {
dst[d + i] = dst[src_start + i];
}
}
}