mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-08-04 15:18:17 +00:00

> Make the root of the workspace a virtual manifest. It might > be tempting to put the main crate into the root, but that > pollutes the root with src/, requires passing --workspace to > every Cargo command, and adds an exception to an otherwise > consistent structure. > Don’t succumb to the temptation to strip common prefix > from folder names. If each crate is named exactly as the > folder it lives in, navigation and renames become easier. > Cargo.tomls of reverse dependencies mention both the folder > and the crate name, it’s useful when they are exactly the > same. Source: https://matklad.github.io/2021/08/22/large-rust-workspaces.html#Smaller-Tips
234 lines
6.4 KiB
Rust
234 lines
6.4 KiB
Rust
use std::cmp::min;
|
|
use std::io;
|
|
|
|
use bitvec::field::BitField as _;
|
|
use bitvec::order::Msb0;
|
|
use bitvec::slice::BitSlice;
|
|
use ironrdp_pdu::codecs::rfx::EntropyAlgorithm;
|
|
use thiserror::Error;
|
|
|
|
use crate::utils::Bits;
|
|
|
|
const KP_MAX: u32 = 80;
|
|
const LS_GR: u32 = 3;
|
|
const UP_GR: u32 = 4;
|
|
const DN_GR: u32 = 6;
|
|
const UQ_GR: u32 = 3;
|
|
const DQ_GR: u32 = 3;
|
|
|
|
macro_rules! write_byte {
|
|
($output:ident, $value:ident) => {
|
|
if !$output.is_empty() {
|
|
$output[0] = $value;
|
|
$output = &mut $output[1..];
|
|
} else {
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! try_split_bits {
|
|
($bits:ident, $n:expr) => {
|
|
if $bits.len() < $n {
|
|
break;
|
|
} else {
|
|
$bits.split_to($n)
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Result<(), RlgrError> {
|
|
let mut k: u32 = 1;
|
|
let mut kr: u32 = 1;
|
|
let mut kp: u32 = k << LS_GR;
|
|
let mut krp: u32 = kr << LS_GR;
|
|
|
|
if tile.is_empty() {
|
|
return Err(RlgrError::EmptyTile);
|
|
}
|
|
|
|
let mut bits = Bits::new(BitSlice::from_slice(tile));
|
|
while !bits.is_empty() && !output.is_empty() {
|
|
match CompressionMode::from(k) {
|
|
CompressionMode::RunLength => {
|
|
let number_of_zeros = truncate_leading_value(&mut bits, false);
|
|
try_split_bits!(bits, 1);
|
|
let run = count_run(number_of_zeros, &mut k, &mut kp) + load_be_u32(try_split_bits!(bits, k as usize));
|
|
|
|
let sign_bit = try_split_bits!(bits, 1).load_be::<u8>();
|
|
|
|
let number_of_ones = truncate_leading_value(&mut bits, true);
|
|
try_split_bits!(bits, 1);
|
|
|
|
let code_remainder = load_be_u32(try_split_bits!(bits, kr as usize)) + ((number_of_ones as u32) << kr);
|
|
|
|
update_parameters_according_to_number_of_ones(number_of_ones, &mut kr, &mut krp);
|
|
kp = kp.saturating_sub(DN_GR);
|
|
k = kp >> LS_GR;
|
|
|
|
let magnitude = compute_rl_magnitude(sign_bit, code_remainder);
|
|
|
|
let size = min(run as usize, output.len());
|
|
fill(&mut output[..size], 0);
|
|
output = &mut output[size..];
|
|
write_byte!(output, magnitude);
|
|
}
|
|
CompressionMode::GolombRice => {
|
|
let number_of_ones = truncate_leading_value(&mut bits, true);
|
|
try_split_bits!(bits, 1);
|
|
|
|
let code_remainder = load_be_u32(try_split_bits!(bits, kr as usize)) + ((number_of_ones as u32) << kr);
|
|
|
|
update_parameters_according_to_number_of_ones(number_of_ones, &mut kr, &mut krp);
|
|
|
|
match mode {
|
|
EntropyAlgorithm::Rlgr1 => {
|
|
let magnitude = compute_rlgr1_magnitude(code_remainder, &mut k, &mut kp);
|
|
write_byte!(output, magnitude);
|
|
}
|
|
EntropyAlgorithm::Rlgr3 => {
|
|
let n_index = compute_n_index(code_remainder);
|
|
|
|
let val1 = load_be_u32(try_split_bits!(bits, n_index));
|
|
let val2 = code_remainder - val1;
|
|
if val1 != 0 && val2 != 0 {
|
|
kp = kp.saturating_sub(2 * DQ_GR);
|
|
k = kp >> LS_GR;
|
|
} else if val1 == 0 && val2 == 0 {
|
|
kp = min(kp + 2 * UQ_GR, KP_MAX);
|
|
k = kp >> LS_GR;
|
|
}
|
|
|
|
let magnitude = compute_rlgr3_magnitude(val1);
|
|
write_byte!(output, magnitude);
|
|
|
|
let magnitude = compute_rlgr3_magnitude(val2);
|
|
write_byte!(output, magnitude);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill remaining buffer with zeros
|
|
fill(output, 0);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn fill(buffer: &mut [i16], value: i16) {
|
|
for v in buffer {
|
|
*v = value;
|
|
}
|
|
}
|
|
|
|
fn load_be_u32(s: &BitSlice<u8, Msb0>) -> u32 {
|
|
if s.is_empty() {
|
|
0
|
|
} else {
|
|
s.load_be::<u32>()
|
|
}
|
|
}
|
|
|
|
// Returns number of truncated bits
|
|
fn truncate_leading_value(bits: &mut Bits<'_>, value: bool) -> usize {
|
|
let leading_values = if value {
|
|
bits.leading_ones()
|
|
} else {
|
|
bits.leading_zeros()
|
|
};
|
|
bits.split_to(leading_values);
|
|
leading_values
|
|
}
|
|
|
|
fn count_run(number_of_zeros: usize, k: &mut u32, kp: &mut u32) -> u32 {
|
|
(0..number_of_zeros)
|
|
.map(|_| {
|
|
let run = 1 << *k;
|
|
*kp = min(*kp + UP_GR, KP_MAX);
|
|
*k = *kp >> LS_GR;
|
|
|
|
run
|
|
})
|
|
.sum()
|
|
}
|
|
|
|
fn compute_rl_magnitude(sign_bit: u8, code_remainder: u32) -> i16 {
|
|
if sign_bit != 0 {
|
|
-((code_remainder + 1) as i16)
|
|
} else {
|
|
(code_remainder + 1) as i16
|
|
}
|
|
}
|
|
|
|
fn compute_rlgr1_magnitude(code_remainder: u32, k: &mut u32, kp: &mut u32) -> i16 {
|
|
if code_remainder == 0 {
|
|
*kp = min(*kp + UQ_GR, KP_MAX);
|
|
*k = *kp >> LS_GR;
|
|
|
|
0
|
|
} else {
|
|
*kp = kp.saturating_sub(DQ_GR);
|
|
*k = *kp >> LS_GR;
|
|
|
|
if code_remainder % 2 != 0 {
|
|
-(((code_remainder + 1) >> 1) as i16)
|
|
} else {
|
|
(code_remainder >> 1) as i16
|
|
}
|
|
}
|
|
}
|
|
|
|
fn compute_rlgr3_magnitude(val: u32) -> i16 {
|
|
if val % 2 != 0 {
|
|
-(((val + 1) >> 1) as i16)
|
|
} else {
|
|
(val >> 1) as i16
|
|
}
|
|
}
|
|
|
|
fn compute_n_index(code_remainder: u32) -> usize {
|
|
if code_remainder == 0 {
|
|
return 0;
|
|
}
|
|
|
|
let code_bytes = code_remainder.to_be_bytes();
|
|
let code_bits = BitSlice::<u8, Msb0>::from_slice(code_bytes.as_ref());
|
|
let leading_zeros = code_bits.leading_zeros();
|
|
|
|
32 - leading_zeros
|
|
}
|
|
|
|
fn update_parameters_according_to_number_of_ones(number_of_ones: usize, kr: &mut u32, krp: &mut u32) {
|
|
if number_of_ones == 0 {
|
|
*krp = (*krp).saturating_sub(2);
|
|
*kr = *krp >> LS_GR;
|
|
} else if number_of_ones > 1 {
|
|
*krp = min(*krp + number_of_ones as u32, KP_MAX);
|
|
*kr = *krp >> LS_GR;
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
enum CompressionMode {
|
|
RunLength,
|
|
GolombRice,
|
|
}
|
|
|
|
impl From<u32> for CompressionMode {
|
|
fn from(m: u32) -> Self {
|
|
if m != 0 {
|
|
Self::RunLength
|
|
} else {
|
|
Self::GolombRice
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum RlgrError {
|
|
#[error("IO error: {0}")]
|
|
IoError(#[from] io::Error),
|
|
#[error("The input tile is empty")]
|
|
EmptyTile,
|
|
}
|