8 Commits

8 changed files with 305 additions and 140 deletions

View File

@@ -3,7 +3,8 @@ name = "upkr"
version = "0.2.0-pre3" version = "0.2.0-pre3"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [profile.release]
strip = "debuginfo"
[dependencies] [dependencies]
cdivsufsort = "2" cdivsufsort = "2"

View File

@@ -39,6 +39,19 @@ build/unpack_armv6m.bin: unpack_armv6m.S
arm-none-eabi-gcc -march=armv6-m -c -o build/unpack_armv6m.o $? arm-none-eabi-gcc -march=armv6-m -c -o build/unpack_armv6m.o $?
arm-none-eabi-objcopy -O binary --only-section=.text build/unpack_armv6m.o $@ arm-none-eabi-objcopy -O binary --only-section=.text build/unpack_armv6m.o $@
build/unpack_arm32: ../c_unpacker/main.c unpack_arm32.S
mkdir -p build
arm-linux-gnueabihf-gcc -g -static -o $@ $^
test_arm32: build/unpack_arm32
qemu-arm $< test_data.upk /tmp/out.bin
cmp test_data.bin /tmp/out.bin
build/unpack_arm32.bin: unpack_arm32.S
mkdir -p build
arm-none-eabi-gcc -c -o build/unpack_arm32.o $?
arm-none-eabi-objcopy -O binary --only-section=.text build/unpack_arm32.o $@
build/unpack_c: ../c_unpacker/main.c ../c_unpacker/unpack.c build/unpack_c: ../c_unpacker/main.c ../c_unpacker/unpack.c
mkdir -p build mkdir -p build
gcc -g -o $@ $^ gcc -g -o $@ $^
@@ -47,5 +60,5 @@ test_c: build/unpack_c
$< test_data.upk /tmp/out.bin $< test_data.upk /tmp/out.bin
cmp test_data.bin /tmp/out.bin cmp test_data.bin /tmp/out.bin
sizes: build/unpack_armv6m.bin build/unpack_riscv64.bin build/unpack_riscv32.bin sizes: build/unpack_armv6m.bin build/unpack_riscv64.bin build/unpack_riscv32.bin build/unpack_arm32.bin
ls -l build/*.bin ls -l build/*.bin

View File

@@ -0,0 +1,100 @@
.arm
.section .text
.global upkr_unpack
.type upkr_unpack, %function
// r0 .. out_ptr (returned)
// r1 .. in_ptr (returned)
// r2 .. state
// r3 .. offset
// r4 .. prev_was_literal / decode_length ret
// r5 .. context index
// r6 .. decode_length temp
// r7 .. probs ptr
// r8-r11 .. decode_bit temp
// r12 .. decode_length return address
upkr_unpack:
push { r3-r11, lr }
mov r2, #384
mov r3, #128
.Lclear:
subs r2, r2, #1
strb r3, [sp, -r2]
bne .Lclear
.Lloop:
mov r5, #0
bl upkr_decode_bit
bcc .Ldata
.Lmatch:
mov r5, #256
rsbs r6, r4, #0
blcc upkr_decode_bit
bcc .Lskip_offset
bl upkr_decode_length
adds r3, r4, #1
popeq { r3-r11, pc }
.Lskip_offset:
mov r5, #256+64
bl upkr_decode_length
.Lcopy_loop:
ldrb r5, [r0, r3]
.Lstore:
strb r5, [r0], #1
adds r4, r4, #1
blt .Lcopy_loop
b .Lloop
.Ldata:
mov r5, #1
.Ldata_loop:
bl upkr_decode_bit
adc r5, r5, r5
movs r4, r5, lsr #8
beq .Ldata_loop
b .Lstore
.type upkr_decode_length, %function
upkr_decode_length:
mov r12, lr
mov r4, #0
mvn r6, #0
.Lbit_loop:
bl upkr_decode_bit_inc
addcc r4, r4, r6
movcc pc, r12
bl upkr_decode_bit_inc
addcs r4, r4, r6
mov r6, r6, lsl #1
b .Lbit_loop
.type upkr_decode_bit, %function
upkr_decode_bit_inc:
add r5, r5, #1
upkr_decode_bit:
cmp r2, #4096
ldrltb r8, [r1], #1
orrlt r2, r8, r2, lsl#8
blt upkr_decode_bit
ldrb r8, [sp, -r5]
and r9, r2, #255
add r9, r9, #1
cmp r8, r9
rsbcs r8, r8, #256
mvn r9, r2, lsr#8
addcs r9, r9, #1
mla r2, r8, r9, r2
add r9, r8, #8
sub r8, r8, r9, lsr#4
rsbcs r8, r8, #256
strb r8, [sp, -r5]
mov pc, r14

View File

@@ -1,13 +1,11 @@
.section .text .section .text
#define FRAME_SIZE (256+32*4+4)
// x8 prob array ptr
// x9 prev was literal // x9 prev was literal
// x10 out ptr // x10 out ptr
// x11 in ptr // x11 in ptr
// x12 offset // x12 offset
// x13 state // x13 state
// x14 context index
.global upkr_unpack .global upkr_unpack
.type upkr_unpack, %function .type upkr_unpack, %function
@@ -15,11 +13,11 @@ upkr_unpack:
mv t4, ra mv t4, ra
mv x17, x8 mv x17, x8
mv t6, x9 mv t6, x9
li x13, FRAME_SIZE li x9, 256 + 128
li x9, 128 mv x13, x9
1: 1:
addi sp, sp, -1 sub x8, sp, x13
sb x9, 0(sp) sb x9, 0(x8)
addi x13, x13, -1 addi x13, x13, -1
bnez x13, 1b bnez x13, 1b
@@ -35,7 +33,7 @@ upkr_unpack:
.Lfinished_offset: .Lfinished_offset:
addi x14, x14, 64 addi x14, x14, 64
jal t3, upkr_decode_number jalr ra // jal upkr_decode_number
1: 1:
add x14, x10, t0 add x14, x10, t0
lbu x14, (x14) lbu x14, (x14)
@@ -58,36 +56,14 @@ upkr_unpack:
.Lread_offset_inc_x14: .Lread_offset_inc_x14:
addi x14, x14, 1 addi x14, x14, 1
.Lread_offset: .Lread_offset:
jal t3, upkr_decode_number jalr ra // jal upkr_decode_number
addi t0, x9, 1 addi t0, x9, 1
bnez t0, .Lfinished_offset bnez t0, .Lfinished_offset
.Ldone: .Ldone:
addi sp, sp, FRAME_SIZE
mv x8, x17 mv x8, x17
mv x9, t6 mv x9, t6
jr t4 jr t4
// x14 context index
// return: x9 negtive decoded number
upkr_decode_number:
mv t5, x14
li x9, 0
li x8, -1
1:
jal upkr_decode_bit
beqz x15, 1f
jal upkr_decode_bit
beqz x15, 2f
add x9, x9, x8
2:
slli x8, x8, 1
j 1b
1:
add x9, x9, x8
mv x14, t5
jr t3
upkr_load_byte: upkr_load_byte:
lbu x15, 0(x11) lbu x15, 0(x11)
addi x11, x11, 1 addi x11, x11, 1
@@ -104,39 +80,52 @@ upkr_decode_bit:
srli x15, x13, 12 srli x15, x13, 12
beqz x15, upkr_load_byte beqz x15, upkr_load_byte
mv t1, x14 addi x14, x14, 1
mv t2, x10
add x14, x14, sp sub t2, sp, x14
lbu x12, 0(x14) lbu x12, (t2)
andi x10, x13, 255 andi x8, x13, 255
sltu x15, x10, x12 sltu x15, x8, x12
srli x13, x13, 8 beqz x15, 1f
beqz x15, .Lelse xori x12, x12, 255
addi x12, x12, 1
1:
srli x8, x13, 8
addi x8, x8, 1
sub x8, x8, x15
mul x8, x8, x12
sub x13, x13, x8
mul x13, x13, x12 addi x8, x12, 8
add x13, x13, x10 srli x8, x8, 4
li x10, 256 + 8 sub x12, x12, x8
sub x10, x10, x12 beqz x15, 1f
srli x10, x10, 4 sub x12, x0, x12
add x12, x12, x10 1:
j .Lendif
.Lelse: sb x12, (t2)
li x16, 256
sub x16, x16, x12
mul x13, x13, x16
add x13, x13, x10
sub x13, x13, x12
addi x10, x12, 8
srli x10, x10, 4
sub x12, x12, x10
.Lendif:
sb x12, 0(x14)
addi x14, t1, 1 jalr ra
mv x10, t2
ret // x14 context index
// return: x9 negtive decoded number
upkr_decode_number:
mv t3, ra
mv t5, x14
li x9, 0
li t1, -1
1:
jal upkr_decode_bit
beqz x15, 1f
jal upkr_decode_bit
beqz x15, 2f
add x9, x9, t1
2:
add t1, t1, t1
j 1b
1:
add x9, x9, t1
mv x14, t5
jr t3

View File

@@ -12,6 +12,8 @@ pub fn pack(
let mut rans_coder = RansCoder::new(config); let mut rans_coder = RansCoder::new(config);
let mut state = lz::CoderState::new(config); let mut state = lz::CoderState::new(config);
let mut literal = vec![];
let mut pos = 0; let mut pos = 0;
while pos < data.len() { while pos < data.len() {
if let Some(ref mut cb) = progress_callback { if let Some(ref mut cb) = progress_callback {
@@ -22,6 +24,10 @@ pub fn pack(
let max_offset = config.max_offset.min(1 << (m.length * 3 - 1).min(31)); let max_offset = config.max_offset.min(1 << (m.length * 3 - 1).min(31));
let offset = pos - m.pos; let offset = pos - m.pos;
if offset < max_offset && m.length >= config.min_length() { if offset < max_offset && m.length >= config.min_length() {
if !literal.is_empty() {
lz::Op::Literal(literal).encode(&mut rans_coder, &mut state, config);
literal = vec![];
}
let length = m.length.min(config.max_length); let length = m.length.min(config.max_length);
lz::Op::Match { lz::Op::Match {
offset: offset as u32, offset: offset as u32,
@@ -43,6 +49,10 @@ pub fn pack(
.count() .count()
.min(config.max_length); .min(config.max_length);
if length >= config.min_length() { if length >= config.min_length() {
if !literal.is_empty() {
lz::Op::Literal(literal).encode(&mut rans_coder, &mut state, config);
literal = vec![];
}
lz::Op::Match { lz::Op::Match {
offset: offset as u32, offset: offset as u32,
len: length as u32, len: length as u32,
@@ -55,11 +65,14 @@ pub fn pack(
} }
if !encoded_match { if !encoded_match {
lz::Op::Literal(data[pos]).encode(&mut rans_coder, &mut state, config); literal.push(data[pos]);
pos += 1; pos += 1;
} }
} }
if !literal.is_empty() {
lz::Op::Literal(literal).encode(&mut rans_coder, &mut state, config);
}
lz::encode_eof(&mut rans_coder, &mut state, config); lz::encode_eof(&mut rans_coder, &mut state, config);
rans_coder.finish() rans_coder.finish()
} }

167
src/lz.rs
View File

@@ -3,36 +3,53 @@ use crate::rans::{EntropyCoder, RansDecoder};
use crate::Config; use crate::Config;
use thiserror::Error; use thiserror::Error;
#[derive(Copy, Clone, Debug)] #[derive(Clone, Debug)]
pub enum Op { pub enum Op {
Literal(u8), Literal(Vec<u8>),
Match { offset: u32, len: u32 }, Match { offset: u32, len: u32 },
} }
impl Op { impl Op {
pub fn encode(&self, coder: &mut dyn EntropyCoder, state: &mut CoderState, config: &Config) { pub fn encode(&self, coder: &mut dyn EntropyCoder, state: &mut CoderState, config: &Config) {
let literal_base = state.pos % state.parity_contexts * 256;
match self { match self {
&Op::Literal(lit) => { &Op::Literal(ref lit) => {
encode_bit(coder, state, literal_base, !config.is_match_bit); assert!(state.prev_was_match);
let mut context_index = 1; encode_length(
for i in (0..8).rev() { coder,
let bit = (lit >> i) & 1 != 0; state,
encode_bit(coder, state, literal_base + context_index, bit); 256 + state.pos % state.parity_contexts * 320,
context_index = (context_index << 1) | bit as usize; lit.len() as u32 + 1,
config,
);
for lit in lit {
let literal_base = state.pos % state.parity_contexts * 320;
let mut context_index = 1;
for i in (0..8).rev() {
let bit = (lit >> i) & 1 != 0;
encode_bit(coder, state, literal_base + context_index, bit);
context_index = (context_index << 1) | bit as usize;
}
state.pos += 1;
} }
state.prev_was_match = false; state.prev_was_match = false;
state.pos += 1;
} }
&Op::Match { offset, len } => { &Op::Match { offset, len } => {
encode_bit(coder, state, literal_base, config.is_match_bit); if state.prev_was_match {
encode_length(
coder,
state,
256 + state.pos % state.parity_contexts * 320,
1,
config,
);
}
let mut new_offset = true; let mut new_offset = true;
if !state.prev_was_match && !config.no_repeated_offsets { if !state.prev_was_match && !config.no_repeated_offsets {
new_offset = offset != state.last_offset; new_offset = offset != state.last_offset;
encode_bit( encode_bit(
coder, coder,
state, state,
256 * state.parity_contexts, 320 * state.parity_contexts,
new_offset == config.new_offset_bit, new_offset == config.new_offset_bit,
); );
} }
@@ -41,14 +58,14 @@ impl Op {
encode_length( encode_length(
coder, coder,
state, state,
256 * state.parity_contexts + 1, 320 * state.parity_contexts + 1,
offset + if config.eof_in_length { 0 } else { 1 }, offset + if config.eof_in_length { 0 } else { 1 },
config, config,
); );
state.last_offset = offset; state.last_offset = offset;
} }
assert!(len as usize >= config.min_length() && len as usize <= config.max_length); assert!(len as usize >= config.min_length() && len as usize <= config.max_length);
encode_length(coder, state, 256 * state.parity_contexts + 65, len, config); encode_length(coder, state, 320 * state.parity_contexts + 65, len, config);
state.prev_was_match = true; state.prev_was_match = true;
state.pos += len as usize; state.pos += len as usize;
} }
@@ -57,25 +74,28 @@ impl Op {
} }
pub fn encode_eof(coder: &mut dyn EntropyCoder, state: &mut CoderState, config: &Config) { pub fn encode_eof(coder: &mut dyn EntropyCoder, state: &mut CoderState, config: &Config) {
encode_bit( if state.prev_was_match {
coder, encode_length(
state, coder,
state.pos % state.parity_contexts * 256, state,
config.is_match_bit, 256 + state.pos % state.parity_contexts * 320,
); 1,
config,
);
}
if !state.prev_was_match && !config.no_repeated_offsets { if !state.prev_was_match && !config.no_repeated_offsets {
encode_bit( encode_bit(
coder, coder,
state, state,
256 * state.parity_contexts, 320 * state.parity_contexts,
config.new_offset_bit ^ config.eof_in_length, config.new_offset_bit ^ config.eof_in_length,
); );
} }
if !config.eof_in_length || state.prev_was_match || config.no_repeated_offsets { if !config.eof_in_length || state.prev_was_match || config.no_repeated_offsets {
encode_length(coder, state, 256 * state.parity_contexts + 1, 1, config); encode_length(coder, state, 320 * state.parity_contexts + 1, 1, config);
} }
if config.eof_in_length { if config.eof_in_length {
encode_length(coder, state, 256 * state.parity_contexts + 65, 1, config); encode_length(coder, state, 320 * state.parity_contexts + 65, 1, config);
} }
} }
@@ -119,9 +139,9 @@ pub struct CoderState {
impl CoderState { impl CoderState {
pub fn new(config: &Config) -> CoderState { pub fn new(config: &Config) -> CoderState {
CoderState { CoderState {
contexts: ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64, config), contexts: ContextState::new((64 + 256) * config.parity_contexts + 1 + 64 + 64, config),
last_offset: 0, last_offset: 0,
prev_was_match: false, prev_was_match: true,
pos: 0, pos: 0,
parity_contexts: config.parity_contexts, parity_contexts: config.parity_contexts,
} }
@@ -168,7 +188,8 @@ pub fn unpack_internal(
max_size: usize, max_size: usize,
) -> Result<isize, UnpackError> { ) -> Result<isize, UnpackError> {
let mut decoder = RansDecoder::new(packed_data, &config); let mut decoder = RansDecoder::new(packed_data, &config);
let mut contexts = ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64, &config); let mut contexts =
ContextState::new((64 + 256) * config.parity_contexts + 1 + 64 + 64, &config);
let mut offset = usize::MAX; let mut offset = usize::MAX;
let mut position = 0usize; let mut position = 0usize;
let mut prev_was_match = false; let mut prev_was_match = false;
@@ -199,50 +220,14 @@ pub fn unpack_internal(
loop { loop {
margin = margin.max(position as isize - decoder.pos() as isize); margin = margin.max(position as isize - decoder.pos() as isize);
let literal_base = position % config.parity_contexts * 256; let literal_length = decode_length(
if decoder.decode_with_context(&mut contexts.context_mut(literal_base))? &mut decoder,
== config.is_match_bit &mut contexts,
{ 256 + position % config.parity_contexts * 320,
if config.no_repeated_offsets config,
|| prev_was_match )? - 1;
|| decoder for _ in 0..literal_length {
.decode_with_context(&mut contexts.context_mut(256 * config.parity_contexts))? let literal_base = position % config.parity_contexts * 320;
== config.new_offset_bit
{
offset = decode_length(
&mut decoder,
&mut contexts,
256 * config.parity_contexts + 1,
&config,
)? - if config.eof_in_length { 0 } else { 1 };
if offset == 0 {
break;
}
}
let length = decode_length(
&mut decoder,
&mut contexts,
256 * config.parity_contexts + 65,
&config,
)?;
if config.eof_in_length && length == 1 {
break;
}
if offset > position {
return Err(UnpackError::OffsetOutOfRange { offset, position });
}
if let Some(ref mut result) = result {
for _ in 0..length {
if result.len() < max_size {
result.push(result[result.len() - offset]);
} else {
break;
}
}
}
position += length;
prev_was_match = true;
} else {
let mut context_index = 1; let mut context_index = 1;
let mut byte = 0; let mut byte = 0;
for i in (0..8).rev() { for i in (0..8).rev() {
@@ -259,6 +244,46 @@ pub fn unpack_internal(
position += 1; position += 1;
prev_was_match = false; prev_was_match = false;
} }
if config.no_repeated_offsets
|| prev_was_match
|| decoder
.decode_with_context(&mut contexts.context_mut(320 * config.parity_contexts))?
== config.new_offset_bit
{
offset = decode_length(
&mut decoder,
&mut contexts,
320 * config.parity_contexts + 1,
&config,
)? - if config.eof_in_length { 0 } else { 1 };
if offset == 0 {
break;
}
}
let length = decode_length(
&mut decoder,
&mut contexts,
320 * config.parity_contexts + 65,
&config,
)?;
if config.eof_in_length && length == 1 {
break;
}
if offset > position {
return Err(UnpackError::OffsetOutOfRange { offset, position });
}
if let Some(ref mut result) = result {
for _ in 0..length {
if result.len() < max_size {
result.push(result[result.len() - offset]);
} else {
break;
}
}
}
position += length;
prev_was_match = true;
} }
if position > max_size { if position > max_size {

View File

@@ -49,6 +49,12 @@ fn main() -> Result<()> {
config.is_match_bit = false; config.is_match_bit = false;
config.new_offset_bit = false; config.new_offset_bit = false;
} }
Long("x86b") => {
config.use_bitstream = true;
config.continue_value_bit = false;
config.no_repeated_offsets = true;
level = 9;
}
Short('u') | Long("unpack") => unpack = true, Short('u') | Long("unpack") => unpack = true,
Long("margin") => calculate_margin = true, Long("margin") => calculate_margin = true,
@@ -154,6 +160,9 @@ fn print_help(exit_code: i32) -> ! {
eprintln!( eprintln!(
" --x86 --bitstream --invert-is-match-bit --invert-continue-value-bit --invert-new-offset-bit" " --x86 --bitstream --invert-is-match-bit --invert-continue-value-bit --invert-new-offset-bit"
); );
eprintln!(
" --x86b --bitstream --invert-continue-value-bit --no-repeated-offsets -9"
);
eprintln!(); eprintln!();
eprintln!("Config options (need to match when packing/unpacking):"); eprintln!("Config options (need to match when packing/unpacking):");
eprintln!(" -b, --bitstream bitstream mode"); eprintln!(" -b, --bitstream bitstream mode");

View File

@@ -15,7 +15,7 @@ pub fn pack(
let mut parse = parse(data, Config::from_level(level), config, progress_cb); let mut parse = parse(data, Config::from_level(level), config, progress_cb);
let mut ops = vec![]; let mut ops = vec![];
while let Some(link) = parse { while let Some(link) = parse {
ops.push(link.op); ops.push(link.op.clone());
parse = link.prev.clone(); parse = link.prev.clone();
} }
let mut state = lz::CoderState::new(config); let mut state = lz::CoderState::new(config);
@@ -32,9 +32,15 @@ struct Parse {
op: lz::Op, op: lz::Op,
} }
struct LiteralPrefix {
arrival: Arrival,
prefix: Vec<u8>,
}
struct Arrival { struct Arrival {
parse: Option<Rc<Parse>>, parse: Option<Rc<Parse>>,
state: lz::CoderState, state: lz::CoderState,
literal_prefix: Option<Box<LiteralPrefix>>,
cost: f64, cost: f64,
} }
@@ -130,6 +136,7 @@ fn parse(
op, op,
})), })),
state, state,
literal_prefix: None,
cost: arrival.cost + cost_counter.cost(), cost: arrival.cost + cost_counter.cost(),
}, },
max_arrivals, max_arrivals,
@@ -141,6 +148,7 @@ fn parse(
Arrival { Arrival {
parse: None, parse: None,
state: lz::CoderState::new(encoding_config), state: lz::CoderState::new(encoding_config),
literal_prefix: None,
cost: 0.0, cost: 0.0,
}, },
max_arrivals, max_arrivals,
@@ -252,19 +260,26 @@ fn parse(
} }
cost_counter.reset(); cost_counter.reset();
let mut state = arrival.state; let (arrival, mut prefix) = if let Some(prefix) = arrival.literal_prefix {
let op = lz::Op::Literal(data[pos]); (prefix.arrival, prefix.prefix)
} else {
(arrival, vec![])
};
let mut state = arrival.state.clone();
prefix.push(data[pos]);
let op = lz::Op::Literal(prefix.clone());
op.encode(cost_counter, &mut state, encoding_config); op.encode(cost_counter, &mut state, encoding_config);
add_arrival( add_arrival(
&mut arrivals, &mut arrivals,
pos + 1, pos + 1,
Arrival { Arrival {
parse: Some(Rc::new(Parse { parse: Some(Rc::new(Parse {
prev: arrival.parse, prev: arrival.parse.clone(),
op, op,
})), })),
state, state,
cost: arrival.cost + cost_counter.cost(), cost: arrival.cost + cost_counter.cost(),
literal_prefix: Some(Box::new(LiteralPrefix { arrival, prefix })),
}, },
max_arrivals, max_arrivals,
); );