#![cfg_attr(docsrs, doc(cfg(feature = "bcj")))]
use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{Algorithm, RawDecoder, RawEncoder, RawProgress};
mod arch;
pub use arch::{Arm, Arm64, ArmThumb, Ia64, Ppc, RiscV, Sparc, X86};
pub trait BcjArch {
const NAME: &'static str;
const EXT: &'static str;
const ALIGN: usize;
type State: Default + Clone + core::fmt::Debug;
fn convert(data: &mut [u8], ip: u32, encode: bool, state: &mut Self::State) -> usize;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoState;
const MAX_INSN: usize = 16;
#[derive(Debug, Clone, Copy, Default)]
pub struct Config {
pub start_offset: u32,
}
#[derive(Debug, Clone)]
struct Bcj<A: BcjArch, const ENCODE: bool> {
hold: Vec<u8>,
pending: Vec<u8>,
head: usize,
ip: u32,
start_ip: u32,
state: A::State,
_arch: core::marker::PhantomData<A>,
}
impl<A: BcjArch, const ENCODE: bool> Bcj<A, ENCODE> {
fn new(cfg: Config) -> Self {
Self {
hold: Vec::new(),
pending: Vec::new(),
head: 0,
ip: cfg.start_offset,
start_ip: cfg.start_offset,
state: A::State::default(),
_arch: core::marker::PhantomData,
}
}
fn reset(&mut self) {
self.hold.clear();
self.pending.clear();
self.head = 0;
self.ip = self.start_ip;
self.state = A::State::default();
}
fn drain_pending(&mut self, out: &mut [u8]) -> usize {
let avail = self.pending.len() - self.head;
let n = avail.min(out.len());
out[..n].copy_from_slice(&self.pending[self.head..self.head + n]);
self.head += n;
if self.head == self.pending.len() {
self.pending.clear();
self.head = 0;
}
n
}
fn convert_hold(&mut self, flush_tail: bool) {
if !self.hold.is_empty() {
let mut scratch = [0u8; MAX_INSN];
let len = self.hold.len();
scratch[..len].copy_from_slice(&self.hold);
let processed = A::convert(&mut scratch[..len], self.ip, ENCODE, &mut self.state);
debug_assert!(processed % A::ALIGN == 0 || A::ALIGN == 1);
if processed > 0 {
self.pending.extend_from_slice(&scratch[..processed]);
self.hold.drain(..processed);
self.ip = self.ip.wrapping_add(processed as u32);
}
}
if flush_tail && !self.hold.is_empty() {
let tail_len = self.hold.len();
self.pending.extend_from_slice(&self.hold);
self.hold.clear();
self.ip = self.ip.wrapping_add(tail_len as u32);
}
}
fn step(&mut self, input: &[u8], output: &mut [u8], final_call: bool) -> (usize, usize, bool) {
let mut consumed = 0usize;
let mut written = 0usize;
loop {
if self.head < self.pending.len() && written < output.len() {
written += self.drain_pending(&mut output[written..]);
}
if written == output.len() && !output.is_empty() {
if self.head < self.pending.len() {
break;
}
}
let before = consumed;
while self.hold.len() < MAX_INSN && consumed < input.len() {
self.hold.push(input[consumed]);
consumed += 1;
}
let refilled = consumed > before;
if final_call {
self.convert_hold(true);
} else if self.hold.len() == MAX_INSN {
self.convert_hold(false);
}
if self.head < self.pending.len() && written < output.len() {
written += self.drain_pending(&mut output[written..]);
}
let pending_left = self.head < self.pending.len();
let can_make_pending =
self.hold.len() == MAX_INSN || (final_call && !self.hold.is_empty());
if !pending_left && !refilled && !can_make_pending {
break;
}
if written == output.len() && !output.is_empty() && pending_left {
break;
}
if output.is_empty() && !refilled {
break;
}
}
let done = final_call && self.hold.is_empty() && self.head >= self.pending.len();
(consumed, written, done)
}
}
macro_rules! bcj_filter {
($(#[$m:meta])* $marker:ident, $arch:ty, $enc:ident, $dec:ident) => {
$(#[$m])*
#[derive(Debug, Clone, Copy, Default)]
pub struct $marker;
#[doc = concat!("Streaming encoder for the `", stringify!($marker), "` filter.")]
#[derive(Debug, Clone)]
pub struct $enc(Bcj<$arch, true>);
#[doc = concat!("Streaming decoder for the `", stringify!($marker), "` filter.")]
#[derive(Debug, Clone)]
pub struct $dec(Bcj<$arch, false>);
impl $enc {
pub fn new(cfg: Config) -> Self {
Self(Bcj::new(cfg))
}
}
impl $dec {
pub fn new(cfg: Config) -> Self {
Self(Bcj::new(cfg))
}
}
impl Algorithm for $marker {
const NAME: &'static str = <$arch as BcjArch>::NAME;
type Encoder = $enc;
type Decoder = $dec;
type EncoderConfig = Config;
type DecoderConfig = Config;
fn encoder_with(cfg: Config) -> $enc {
$enc::new(cfg)
}
fn decoder_with(cfg: Config) -> $dec {
$dec::new(cfg)
}
}
impl RawEncoder for $enc {
fn raw_encode(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<RawProgress, Error> {
let (consumed, written, _) = self.0.step(input, output, false);
Ok(RawProgress { consumed, written, done: false })
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
let (_, written, done) = self.0.step(&[], output, true);
Ok(RawProgress { consumed: 0, written, done })
}
fn raw_reset(&mut self) {
self.0.reset();
}
}
impl RawDecoder for $dec {
fn raw_decode(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<RawProgress, Error> {
let (consumed, written, _) = self.0.step(input, output, false);
Ok(RawProgress { consumed, written, done: false })
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
let (_, written, done) = self.0.step(&[], output, true);
Ok(RawProgress { consumed: 0, written, done })
}
fn raw_reset(&mut self) {
self.0.reset();
}
}
impl Default for $enc {
fn default() -> Self {
Self::new(Config::default())
}
}
impl Default for $dec {
fn default() -> Self {
Self::new(Config::default())
}
}
};
}
bcj_filter!(
BcjX86, X86, X86Encoder, X86Decoder
);
bcj_filter!(
BcjArm, Arm, ArmEncoder, ArmDecoder
);
bcj_filter!(
BcjArmThumb, ArmThumb, ArmThumbEncoder, ArmThumbDecoder
);
bcj_filter!(
BcjArm64, Arm64, Arm64Encoder, Arm64Decoder
);
bcj_filter!(
BcjPpc, Ppc, PpcEncoder, PpcDecoder
);
bcj_filter!(
BcjSparc, Sparc, SparcEncoder, SparcDecoder
);
bcj_filter!(
BcjIa64, Ia64, Ia64Encoder, Ia64Decoder
);
bcj_filter!(
BcjRiscV, RiscV, RiscVEncoder, RiscVDecoder
);