use std::fmt::Debug;
use std::io::{Cursor, Read, Write};
use std::num::NonZeroU32;
use crate::LeptonError;
use crate::consts::JpegType;
use crate::enabled_features::EnabledFeatures;
use crate::helpers::*;
use crate::lepton_error::{AddContext, ExitCode, Result, err_exit_code};
use super::component_info::ComponentInfo;
use super::jpeg_code;
use super::truncate_components::TruncateComponents;
#[derive(Debug, Default, Clone)]
pub struct RestartSegmentCodingInfo {
pub overhang_byte: u8,
pub num_overhang_bits: u8,
pub luma_y_start: u32,
pub luma_y_end: u32,
pub last_dc: [i16; 4],
}
impl RestartSegmentCodingInfo {
pub fn new(
overhang_byte: u8,
num_overhang_bits: u8,
last_dc: [i16; 4],
mcu: u32,
jf: &JpegHeader,
) -> Self {
let mcu_y = mcu / jf.mcuh;
let luma_mul = jf.cmp_info[0].bcv / jf.mcuv;
Self {
overhang_byte,
num_overhang_bits,
last_dc,
luma_y_start: luma_mul * mcu_y,
luma_y_end: luma_mul * (mcu_y + 1),
}
}
}
#[derive(Default, Clone, Debug)]
pub struct ReconstructionInfo {
pub max_cmp: u32,
pub max_bpos: u32,
pub max_sah: u8,
pub max_dpos: [u32; 4],
pub early_eof_encountered: bool,
pub pad_bit: Option<u8>,
pub rst_cnt: Vec<u32>,
pub rst_cnt_set: bool,
pub truncate_components: TruncateComponents,
pub rst_err: Vec<u8>,
pub raw_jpeg_header: Vec<u8>,
pub garbage_data: Vec<u8>,
}
pub fn parse_jpeg_header<R: Read>(
reader: &mut R,
enabled_features: &EnabledFeatures,
jpeg_header: &mut JpegHeader,
rinfo: &mut ReconstructionInfo,
) -> Result<bool> {
let mut output = Vec::new();
let mut output_cursor = Cursor::new(&mut output);
let mut mirror = Mirror::new(reader, &mut output_cursor);
if jpeg_header.parse(&mut mirror, enabled_features).context()? {
rinfo.raw_jpeg_header.append(&mut output);
return Ok(true);
} else {
if output.len() > 2 {
rinfo.raw_jpeg_header.extend(&output[0..output.len() - 2]);
}
return Ok(false);
}
}
struct Mirror<'a, R, W> {
read: &'a mut R,
output: &'a mut W,
amount_written: usize,
}
impl<'a, R, W> Mirror<'a, R, W> {
pub fn new(read: &'a mut R, output: &'a mut W) -> Self {
Mirror {
read,
output,
amount_written: 0,
}
}
}
impl<R: Read, W: Write> Read for Mirror<'_, R, W> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let n = self.read.read(buf)?;
self.output.write_all(&buf[..n])?;
self.amount_written += n;
Ok(n)
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct HuffCodes {
pub c_val: [u16; 256],
pub c_len: [u16; 256],
pub c_len_plus_s: [u8; 256],
pub c_val_shift_s: [u32; 512],
pub max_eob_run: u16,
}
impl Default for HuffCodes {
fn default() -> Self {
HuffCodes {
c_val: [0; 256],
c_len: [0; 256],
c_len_plus_s: [0; 256],
c_val_shift_s: [0; 512],
max_eob_run: 0,
}
}
}
impl HuffCodes {
pub fn construct_from_segment(segment: &[u8]) -> Result<Self> {
let clen_offset = 0;
let cval_offset = 16;
let mut hc = HuffCodes::default();
let mut k = 0;
let mut code = 0;
for i in 0..16 {
ensure_space(segment, clen_offset, i + 1).context()?;
let mut j = 0;
while j < segment[clen_offset + (i & 0xff)] {
ensure_space(segment, cval_offset, k + 1).context()?;
let len = (1 + i) as u16;
if u32::from(code) >= (1u32 << len) {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"invalid huffman code layout, too many codes for a given length",
);
}
hc.c_len[usize::from(segment[cval_offset + (k & 0xff)])] = len;
hc.c_val[usize::from(segment[cval_offset + (k & 0xff)])] = code;
if code == 65535 {
return err_exit_code(ExitCode::UnsupportedJpeg, "huffman code too large");
}
k += 1;
code += 1;
j += 1;
}
code = code << 1;
}
hc.post_initialize();
Ok(hc)
}
fn post_initialize(&mut self) {
for i in 0..256 {
let s = i & 0xf;
self.c_len_plus_s[i] = (self.c_len[i] + (s as u16)) as u8;
self.c_val_shift_s[i] = u32::from(self.c_val[i]) << s;
self.c_val_shift_s[i + 256] = (u32::from(self.c_val[i]) << s) | ((1u32 << s) - 1);
}
self.max_eob_run = 0;
let mut i: i32 = 14;
while i >= 0 {
if self.c_len[((i << 4) & 0xff) as usize] > 0 {
self.max_eob_run = ((2 << i) - 1) as u16;
break;
}
i -= 1;
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct HuffTree {
pub node: [[u16; 2]; 256],
pub peek_code: [(u8, u8); 256],
}
impl Default for HuffTree {
fn default() -> Self {
HuffTree {
node: [[0; 2]; 256],
peek_code: [(0, 0); 256],
}
}
}
impl HuffTree {
pub fn construct_hufftree(hc: &HuffCodes, accept_invalid_dht: bool) -> Result<Self> {
let mut ht = HuffTree::default();
let mut nextfree = 1;
for i in 0..256 {
let mut node = 0;
if hc.c_len[i] > 0 {
let mut j = hc.c_len[i] - 1;
while j > 0 {
if node <= 0xff {
if bitn(hc.c_val[i], j) == 1 {
if ht.node[node][1] == 0 {
ht.node[node][1] = nextfree;
nextfree += 1;
}
node = usize::from(ht.node[node][1]);
} else {
if ht.node[node][0] == 0 {
ht.node[node][0] = nextfree;
nextfree += 1;
}
node = usize::from(ht.node[node][0]);
}
} else {
if !accept_invalid_dht {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"Huffman table out of space",
);
}
}
j -= 1;
}
}
if node <= 0xff {
if hc.c_len[i] > 0 {
if bitn(hc.c_val[i], 0) == 1 {
ht.node[node][1] = (i + 256) as u16;
} else {
ht.node[node][0] = (i + 256) as u16;
}
}
} else {
if !accept_invalid_dht {
return err_exit_code(ExitCode::UnsupportedJpeg, "Huffman table out of space");
}
}
}
for x in &mut ht.node {
if x[0] == 0 {
x[0] = 0xffff;
}
if x[1] == 0 {
x[1] = 0xffff;
}
}
for peekbyte in 0..256 {
let mut node = 0;
let mut len: u8 = 0;
while node < 256 && len <= 7 {
node = ht.node[usize::from(node)][(peekbyte >> (7 - len)) & 0x1];
len += 1;
}
if node == 0xffff || node < 256 {
ht.peek_code[peekbyte] = (0, 0xff);
} else {
ht.peek_code[peekbyte] = ((node - 256) as u8, len);
}
}
Ok(ht)
}
}
#[derive(Clone)]
pub struct JpegHeader {
pub q_tables: [[u16; 64]; 4],
h_codes: [[HuffCodes; 4]; 2],
h_trees: [[HuffTree; 4]; 2],
ht_set: [[u8; 4]; 2],
pub cmp_info: [ComponentInfo; 4],
pub cmpc: usize,
pub img_width: u32,
pub img_height: u32,
pub jpeg_type: JpegType,
pub sfhm: u32,
pub sfvm: u32,
pub mcuv: NonZeroU32,
pub mcuh: NonZeroU32,
pub mcuc: u32,
pub rsti: u32,
pub cs_cmpc: usize,
pub cs_cmp: [usize; 4],
pub cs_from: u8,
pub cs_to: u8,
pub cs_sah: u8,
pub cs_sal: u8,
}
impl std::fmt::Debug for JpegHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JpegHeader")
.field("cmp_info", &self.cmp_info)
.field("cmpc", &self.cmpc)
.field("img_width", &self.img_width)
.field("img_height", &self.img_height)
.field("jpeg_type", &self.jpeg_type)
.field("sfhm", &self.sfhm)
.field("sfvm", &self.sfvm)
.field("mcuv", &self.mcuv)
.field("mcuh", &self.mcuh)
.field("mcuc", &self.mcuc)
.field("rsti", &self.rsti)
.field("cs_cmpc", &self.cs_cmpc)
.field("cs_cmp", &self.cs_cmp)
.field("cs_from", &self.cs_from)
.field("cs_to", &self.cs_to)
.field("cs_sah", &self.cs_sah)
.field("cs_sal", &self.cs_sal)
.finish()
}
}
enum ParseSegmentResult {
Continue,
EOI,
SOS,
}
impl Default for JpegHeader {
fn default() -> Self {
return JpegHeader {
q_tables: [[0; 64]; 4],
h_codes: [[HuffCodes::default(); 4]; 2],
h_trees: [[HuffTree::default(); 4]; 2],
ht_set: [[0; 4]; 2],
cmp_info: [
ComponentInfo::default(),
ComponentInfo::default(),
ComponentInfo::default(),
ComponentInfo::default(),
],
cmpc: 0,
img_width: 0,
img_height: 0,
jpeg_type: JpegType::Unknown,
sfhm: 0,
sfvm: 0,
mcuv: NonZeroU32::MIN,
mcuh: NonZeroU32::MIN,
mcuc: 0,
rsti: 0,
cs_cmpc: 0,
cs_from: 0,
cs_to: 0,
cs_sah: 0,
cs_sal: 0,
cs_cmp: [0; 4],
};
}
}
impl JpegHeader {
pub fn is_single_scan(&self) -> bool {
assert!(self.jpeg_type != JpegType::Unknown);
self.jpeg_type == JpegType::Sequential && self.cmpc == self.cs_cmpc
}
#[inline(always)]
pub(super) fn get_huff_dc_codes(&self, cmp: usize) -> &HuffCodes {
&self.h_codes[0][usize::from(self.cmp_info[cmp].huff_dc)]
}
#[inline(always)]
pub(super) fn get_huff_dc_tree(&self, cmp: usize) -> &HuffTree {
&self.h_trees[0][usize::from(self.cmp_info[cmp].huff_dc)]
}
#[inline(always)]
pub(super) fn get_huff_ac_codes(&self, cmp: usize) -> &HuffCodes {
&self.h_codes[1][usize::from(self.cmp_info[cmp].huff_ac)]
}
#[inline(always)]
pub(super) fn get_huff_ac_tree(&self, cmp: usize) -> &HuffTree {
&self.h_trees[1][usize::from(self.cmp_info[cmp].huff_ac)]
}
pub fn parse<R: Read>(
&mut self,
reader: &mut R,
enabled_features: &EnabledFeatures,
) -> Result<bool> {
loop {
match self
.parse_next_segment(reader, enabled_features)
.context()?
{
ParseSegmentResult::EOI => {
return Ok(false);
}
ParseSegmentResult::SOS => {
break;
}
_ => {}
}
}
if self.cmpc == 0 {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"header contains incomplete information",
);
}
for cmp in 0..self.cmpc {
if (self.cmp_info[cmp].sfv == 0)
|| (self.cmp_info[cmp].sfh == 0)
|| (self.q_tables[usize::from(self.cmp_info[cmp].q_table_index)][0] == 0)
|| (self.jpeg_type == JpegType::Unknown)
{
return err_exit_code(
ExitCode::UnsupportedJpeg,
"header contains incomplete information (components)",
);
}
}
for cmp in 0..self.cmpc {
if self.cmp_info[cmp].sfh > self.sfhm {
self.sfhm = self.cmp_info[cmp].sfh;
}
if self.cmp_info[cmp].sfv > self.sfvm {
self.sfvm = self.cmp_info[cmp].sfv;
}
}
self.mcuv = NonZeroU32::new(
(1.0 * self.img_height as f64 / (8.0 * self.sfhm as f64)).ceil() as u32,
)
.ok_or_else(|| LeptonError::new(ExitCode::UnsupportedJpeg, "mcuv is zero"))?;
self.mcuh =
NonZeroU32::new((1.0 * self.img_width as f64 / (8.0 * self.sfvm as f64)).ceil() as u32)
.ok_or_else(|| LeptonError::new(ExitCode::UnsupportedJpeg, "mcuh is zero"))?;
self.mcuc = self.mcuv.get() * self.mcuh.get();
for cmp in 0..self.cmpc {
self.cmp_info[cmp].mbs = self.cmp_info[cmp].sfv * self.cmp_info[cmp].sfh;
self.cmp_info[cmp].bcv = self.mcuv.get() * self.cmp_info[cmp].sfh;
self.cmp_info[cmp].bch = self.mcuh.get() * self.cmp_info[cmp].sfv;
self.cmp_info[cmp].bc = self.cmp_info[cmp].bcv * self.cmp_info[cmp].bch;
self.cmp_info[cmp].ncv = (1.0
* self.img_height as f64
* (self.cmp_info[cmp].sfh as f64 / (8.0 * self.sfhm as f64)))
.ceil() as u32;
self.cmp_info[cmp].nch = (1.0
* self.img_width as f64
* (self.cmp_info[cmp].sfv as f64 / (8.0 * self.sfvm as f64)))
.ceil() as u32;
self.cmp_info[cmp].nc = self.cmp_info[cmp].ncv * self.cmp_info[cmp].nch;
}
if self.cmpc <= 3 {
for cmp in 0..self.cmpc {
self.cmp_info[cmp].sid = cmp as u32;
}
} else {
for cmp in 0..self.cmpc {
self.cmp_info[cmp].sid = 0;
}
}
return Ok(true);
}
pub fn verify_huffman_table(&self, dc_present: bool, ac_present: bool) -> Result<()> {
for icsc in 0..self.cs_cmpc {
let icmp = self.cs_cmp[icsc];
if dc_present && self.ht_set[0][self.cmp_info[icmp].huff_dc as usize] == 0 {
return err_exit_code(
ExitCode::UnsupportedJpeg,
format!("DC huffman table missing for component {0}", icmp),
);
} else if ac_present && self.ht_set[1][self.cmp_info[icmp].huff_ac as usize] == 0 {
return err_exit_code(
ExitCode::UnsupportedJpeg,
format!("AC huffman table missing for component {0}", icmp),
);
}
}
Ok(())
}
fn parse_next_segment<R: Read>(
&mut self,
reader: &mut R,
enabled_features: &EnabledFeatures,
) -> Result<ParseSegmentResult> {
let mut header = [0u8; 4];
if reader.read(&mut header[0..1]).context()? == 0 {
return Ok(ParseSegmentResult::EOI);
}
if header[0] != 0xff {
return err_exit_code(ExitCode::UnsupportedJpeg, "invalid header encountered");
}
reader.read_exact(&mut header[1..2]).context()?;
if header[1] == jpeg_code::EOI {
return Ok(ParseSegmentResult::EOI);
}
reader.read_exact(&mut header[2..]).context()?;
let mut segment_data = Vec::new();
let segment_size = b_short(header[2], header[3]);
if segment_size < 2 {
return err_exit_code(ExitCode::UnsupportedJpeg, "segment is too short");
}
segment_data.resize(usize::from(segment_size) - 2, 0);
reader.read_exact(&mut segment_data).context()?;
let mut hpos = 0;
let len = segment_data.len();
let segment = &segment_data[..];
let btype = header[1];
match btype
{
jpeg_code::DHT => {
while hpos < len
{
let lval = usize::from(lbits(segment[hpos], 4));
let rval = usize::from(rbits(segment[hpos], 4));
if (lval >= 2) || (rval >= 4)
{
break;
}
hpos+=1;
self.h_codes[lval][rval] = HuffCodes::construct_from_segment(&segment[hpos..]).context()?;
self.h_trees[lval][rval] = HuffTree::construct_hufftree(&self.h_codes[lval][rval], enabled_features.accept_invalid_dht).context()?;
self.ht_set[lval][rval] = 1;
let mut skip = 16;
ensure_space(segment,hpos, 16)?;
for i in 0..16
{
skip += usize::from(segment[hpos + i]);
}
hpos += skip;
}
if hpos != len
{
return err_exit_code(ExitCode::UnsupportedJpeg,"size mismatch in dht marker");
}
}
jpeg_code::DQT => {
while hpos < len
{
let lval = usize::from(lbits(segment[hpos], 4));
let rval = usize::from(rbits(segment[hpos], 4));
if lval >= 2 || rval >= 4
{
return err_exit_code(ExitCode::UnsupportedJpeg,"DQT has invalid index");
}
hpos+=1;
if lval == 0
{
ensure_space(segment,hpos, 64).context()?;
for i in 0..64
{
self.q_tables[rval][i] = segment[hpos + i] as u16;
if self.q_tables[rval][i] == 0
{
if enabled_features.reject_dqts_with_zeros
{
return err_exit_code(ExitCode::UnsupportedJpegWithZeroIdct0,"DQT has zero value");
}
else {
break;
}
}
}
hpos += 64;
}
else
{
ensure_space(segment,hpos, 128).context()?;
for i in 0..64
{
self.q_tables[rval][i] = b_short(segment[hpos + (2 * i)], segment[hpos + (2 * i) + 1]);
if self.q_tables[rval][i] == 0
{
if enabled_features.reject_dqts_with_zeros
{
return err_exit_code(ExitCode::UnsupportedJpegWithZeroIdct0,"DQT has zero value");
}
else {
break;
}
}
}
hpos += 128;
}
}
if hpos != len
{
return err_exit_code(ExitCode::UnsupportedJpeg, "size mismatch in dqt marker");
}
}
jpeg_code::DRI =>
{ ensure_space(segment,hpos, 2).context()?;
self.rsti = u32::from(b_short(segment[hpos], segment[hpos + 1]));
}
jpeg_code::SOS => {
ensure_space(segment,hpos, 1).context()?;
self.cs_cmpc = usize::from(segment[hpos]);
if self.cs_cmpc == 0
{
return err_exit_code( ExitCode::UnsupportedJpeg, "zero components in scan");
}
if self.cs_cmpc > self.cmpc
{
return err_exit_code( ExitCode::UnsupportedJpeg, format!("{0} components in scan, only {1} are allowed", self.cs_cmpc, self.cmpc));
}
hpos+=1;
for i in 0..self.cs_cmpc
{
ensure_space(segment,hpos, 2).context()?;
let mut cmp = 0;
while cmp < self.cmpc && segment[hpos] != self.cmp_info[cmp].jid
{
cmp+=1;
}
if cmp == self.cmpc
{
return err_exit_code(ExitCode::UnsupportedJpeg, "component id mismatch in start-of-scan");
}
self.cs_cmp[i] = cmp;
self.cmp_info[cmp].huff_dc = lbits(segment[hpos + 1], 4);
self.cmp_info[cmp].huff_ac = rbits(segment[hpos + 1], 4);
if (self.cmp_info[cmp].huff_dc == 0xff) || (self.cmp_info[cmp].huff_dc >= 4) ||
(self.cmp_info[cmp].huff_ac == 0xff) || (self.cmp_info[cmp].huff_ac >= 4)
{
return err_exit_code(ExitCode::UnsupportedJpeg,"huffman table number mismatch");
}
hpos += 2;
}
ensure_space(segment,hpos, 3).context()?;
self.cs_from = segment[hpos + 0];
self.cs_to = segment[hpos + 1];
self.cs_sah = lbits(segment[hpos + 2], 4);
self.cs_sal = rbits(segment[hpos + 2], 4);
if (self.cs_from > self.cs_to) || (self.cs_from > 63) || (self.cs_to > 63)
{
return err_exit_code(ExitCode::UnsupportedJpeg,"spectral selection parameter out of range");
}
if (self.cs_sah >= 12) || (self.cs_sal >= 12)
{
return err_exit_code(ExitCode::UnsupportedJpeg, "successive approximation parameter out of range");
}
return Ok(ParseSegmentResult::SOS);
}
jpeg_code::SOF0| jpeg_code::SOF1| jpeg_code::SOF2 => {
if self.jpeg_type != JpegType::Unknown
{
return err_exit_code(ExitCode::UnsupportedJpeg, "image cannot have multiple SOF blocks");
}
if btype == jpeg_code::SOF2
{
self.jpeg_type = JpegType::Progressive;
}
else
{
self.jpeg_type = JpegType::Sequential;
}
ensure_space(segment,hpos, 6).context()?;
let lval = segment[hpos];
if lval != 8
{
return err_exit_code(ExitCode::UnsupportedJpeg, format!("{0} bit data precision is not supported", lval));
}
self.img_height = u32::from(b_short(segment[hpos + 1], segment[hpos + 2]));
self.img_width = u32::from(b_short(segment[hpos + 3], segment[hpos + 4]));
if self.img_height == 0 || self.img_width == 0
{
return err_exit_code(ExitCode::UnsupportedJpeg, "image dimensions can't be zero");
}
if self.img_height > enabled_features.max_jpeg_height || self.img_width > enabled_features.max_jpeg_width
{
return err_exit_code(ExitCode::UnsupportedJpeg, format!("image dimensions larger than {0}x{1}", enabled_features.max_jpeg_width, enabled_features.max_jpeg_height));
}
self.cmpc = usize::from(segment[hpos + 5]);
if self.cmpc > 4
{
return err_exit_code(ExitCode::UnsupportedJpeg, format!("image has {0} components, max 4 are supported", self.cmpc));
}
hpos += 6;
for cmp in 0..self.cmpc
{
ensure_space(segment,hpos, 3).context()?;
self.cmp_info[cmp].jid = segment[hpos];
self.cmp_info[cmp].sfv = u32::from(lbits(segment[hpos + 1], 4));
self.cmp_info[cmp].sfh = u32::from(rbits(segment[hpos + 1], 4));
if self.cmp_info[cmp].sfv > 2 || self.cmp_info[cmp].sfh > 2
{
return err_exit_code(ExitCode::SamplingBeyondTwoUnsupported, "Sampling type beyond to not supported");
}
let quantization_table_value = segment[hpos + 2];
if usize::from(quantization_table_value) >= self.q_tables.len()
{
return err_exit_code(ExitCode::UnsupportedJpeg,"quantizationTableValue too big");
}
self.cmp_info[cmp].q_table_index = quantization_table_value;
hpos += 3;
}
}
0xC3 => {
return err_exit_code(ExitCode::UnsupportedJpeg,"sof3 marker found, image is coded lossless");
}
0xC5 => {
return err_exit_code(ExitCode::UnsupportedJpeg,"sof5 marker found, image is coded diff. sequential");
}
0xC6 => {
return err_exit_code(ExitCode::UnsupportedJpeg,"sof6 marker found, image is coded diff. progressive");
}
0xC7 => {
return err_exit_code(ExitCode::UnsupportedJpeg,"sof7 marker found, image is coded diff. lossless");
}
0xC9 => {
return err_exit_code(ExitCode::UnsupportedJpeg, "sof9 marker found, image is coded arithm. sequential");
}
0xCA => {
return err_exit_code(ExitCode::UnsupportedJpeg, "sof10 marker found, image is coded arithm. progressive");
}
0xCB => {
return err_exit_code(ExitCode::UnsupportedJpeg, "sof11 marker found, image is coded arithm. lossless");
}
0xCD => {
return err_exit_code(ExitCode::UnsupportedJpeg, "sof13 marker found, image is coded arithm. diff. sequential");
}
0xCE => {
return err_exit_code(ExitCode::UnsupportedJpeg, "sof14 marker found, image is coded arithm. diff. progressive");
}
0xCF => {
return err_exit_code(ExitCode::UnsupportedJpeg, "sof15 marker found, image is coded arithm. diff. lossless");
}
0xE0| 0xE1| 0xE2| 0xE3| 0xE4| 0xE5| 0xE6| 0xE7| 0xE8| 0xE9| 0xEA| 0xEB| 0xEC| 0xED| 0xEE| 0xEF| 0xFE => {}
jpeg_code::RST0| 0xD1| 0xD2| 0xD3| 0xD4| 0xD5| 0xD6| 0xD7 => {
return err_exit_code(ExitCode::UnsupportedJpeg, "rst marker found out of place");
}
jpeg_code::SOI => {
return err_exit_code(ExitCode::UnsupportedJpeg, "soi marker found out of place");
}
jpeg_code::EOI => {
return err_exit_code(ExitCode::UnsupportedJpeg,"eoi marker found out of place");
}
_ => {
return err_exit_code(ExitCode::UnsupportedJpeg, format!("unknown marker found: FF {0:X}", btype));
}
}
return Ok(ParseSegmentResult::Continue);
}
}
fn ensure_space(segment: &[u8], hpos: usize, amount: usize) -> Result<()> {
if hpos + amount > segment.len() {
return err_exit_code(ExitCode::UnsupportedJpeg, "SOF too small");
}
Ok(())
}
#[cfg(any(test, feature = "micro_benchmark"))]
pub(super) fn generate_huff_table_from_distribution(freq: &[usize; 256]) -> HuffCodes {
use std::collections::{BinaryHeap, HashMap};
struct Node {
symbol: Option<u8>,
freq: usize,
left: Option<Box<Node>>,
right: Option<Box<Node>>,
}
impl PartialOrd for Node {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.freq.cmp(&other.freq).reverse())
}
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.freq == other.freq
}
}
impl Eq for Node {}
impl Ord for Node {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.freq.cmp(&other.freq).reverse()
}
}
fn build_tree(freq: &[usize]) -> Box<Node> {
let mut pq = BinaryHeap::new();
for (symbol, &freq) in freq.iter().enumerate() {
if freq > 0 {
pq.push(Box::new(Node {
symbol: Some(symbol as u8),
freq,
left: None,
right: None,
}));
}
}
while pq.len() > 1 {
let left = pq.pop().unwrap();
let right = pq.pop().unwrap();
let new_node = Node {
symbol: None,
freq: left.freq + right.freq,
left: Some(left),
right: Some(right),
};
pq.push(Box::new(new_node));
}
pq.pop().unwrap()
}
fn generate_codes(root: &Node, codes: &mut HashMap<u8, (u16, u8)>, prefix: u16, length: u8) {
if let Some(symbol) = root.symbol {
codes.insert(symbol, (prefix, length));
} else {
if let Some(ref left) = root.left {
generate_codes(left, codes, prefix << 1, length + 1);
}
if let Some(ref right) = root.right {
generate_codes(right, codes, (prefix << 1) | 1, length + 1);
}
}
}
let root = build_tree(freq);
let mut codes = HashMap::new();
generate_codes(&root, &mut codes, 0, 0);
let mut retval = HuffCodes::default();
for (&symbol, &(code, length)) in &codes {
retval.c_len[symbol as usize] = length.into();
retval.c_val[symbol as usize] = code;
}
retval.post_initialize();
retval
}