1 Commits

Author SHA1 Message Date
7d40bb8123 hacked parity context version for arm code 2021-12-30 11:49:59 +01:00
19 changed files with 90 additions and 823 deletions

14
Cargo.lock generated
View File

@@ -62,12 +62,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexopt"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.108" version = "0.2.108"
@@ -95,6 +89,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "pico-args"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]] [[package]]
name = "sacabase" name = "sacabase"
version = "2.0.0" version = "2.0.0"
@@ -121,8 +121,8 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cdivsufsort", "cdivsufsort",
"lexopt",
"pbr", "pbr",
"pico-args",
] ]
[[package]] [[package]]

View File

@@ -1,12 +1,12 @@
[package] [package]
name = "upkr" name = "upkr"
version = "0.2.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
cdivsufsort = "2" cdivsufsort = "2"
lexopt = "0.2.1" pico-args = "0.4"
anyhow = "1" anyhow = "1"
pbr = "1" pbr = "1"

View File

@@ -1 +0,0 @@
/build/

View File

@@ -1,36 +0,0 @@
build/unpack_riscv64: ../c_unpacker/main.c unpack_riscv.S
mkdir -p build
riscv64-linux-gnu-gcc -g -static -o $@ $^
test_riscv64: build/unpack_riscv64
qemu-riscv64 $< test_data.upk /tmp/out.bin
cmp test_data.bin /tmp/out.bin
build/unpack_riscv64.bin: unpack_riscv.S
mkdir -p build
riscv64-linux-gnu-gcc -c -o build/unpack_riscv64.o $?
riscv64-linux-gnu-objcopy -O binary --only-section=.text build/unpack_riscv64.o $@
build/unpack_armv6m: ../c_unpacker/main.c unpack_armv6m.S
mkdir -p build
arm-linux-gnueabihf-gcc -g -static -o $@ $^
test_armv6m: build/unpack_armv6m
qemu-arm $< test_data.upk /tmp/out.bin
cmp test_data.bin /tmp/out.bin
build/unpack_armv6m.bin: unpack_armv6m.S
mkdir -p build
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 $@
build/unpack_c: ../c_unpacker/main.c ../c_unpacker/unpack.c
mkdir -p build
gcc -g -o $@ $^
test_c: build/unpack_c
$< test_data.upk /tmp/out.bin
cmp test_data.bin /tmp/out.bin
sizes: build/unpack_armv6m.bin build/unpack_riscv64.bin
ls -l build/*.bin

View File

@@ -1,99 +0,0 @@
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
u8* upkr_data_ptr;
u8 upkr_probs[1 + 255 + 1 + 2*32 + 2*32];
#ifdef UPKR_BITSTREAM
u16 upkr_state;
u8 upkr_current_byte;
int upkr_bits_left;
#else
u32 upkr_state;
#endif
int upkr_decode_bit(int context_index) {
#ifdef UPKR_BITSTREAM
while(upkr_state < 32768) {
if(upkr_bits_left == 0) {
upkr_current_byte = *upkr_data_ptr++;
upkr_bits_left = 8;
}
upkr_state = (upkr_state << 1) + (upkr_current_byte & 1);
upkr_current_byte >>= 1;
--upkr_bits_left;
}
#else
while(upkr_state < 4096) {
upkr_state = (upkr_state << 8) | *upkr_data_ptr++;
}
#endif
int prob = upkr_probs[context_index];
int bit = (upkr_state & 255) < prob ? 1 : 0;
int tmp = prob;
if(!bit) {
tmp = 256 - tmp;
}
upkr_state = tmp * (upkr_state >> 8) + (upkr_state & 255);
tmp += (256 - tmp + 8) >> 4;
if(!bit) {
upkr_state -= prob;
tmp = 256 - tmp;
}
upkr_probs[context_index] = tmp;
return bit;
}
int upkr_decode_length(int context_index) {
int length = 0;
int bit_pos = 0;
while(upkr_decode_bit(context_index)) {
length |= upkr_decode_bit(context_index + 1) << bit_pos++;
context_index += 2;
}
return length | (1 << bit_pos);
}
void* upkr_unpack(void* destination, void* compressed_data) {
upkr_data_ptr = (u8*)compressed_data;
upkr_state = 0;
#ifdef UPKR_BITSTREAM
upkr_bits_left = 0;
#endif
for(int i = 0; i < sizeof(upkr_probs); ++i)
upkr_probs[i] = 128;
u8* write_ptr = (u8*)destination;
int prev_was_match = 0;
int offset = 0;
for(;;) {
if(upkr_decode_bit(0)) {
if(prev_was_match || upkr_decode_bit(256)) {
offset = upkr_decode_length(257) - 1;
if(offset == 0) {
break;
}
}
int length = upkr_decode_length(257 + 64);
while(length--) {
*write_ptr = write_ptr[-offset];
++write_ptr;
}
prev_was_match = 1;
} else {
int byte = 1;
while(byte < 256) {
int bit = upkr_decode_bit(byte);
byte = (byte << 1) + bit;
}
*write_ptr++ = byte;
prev_was_match = 0;
}
}
return write_ptr;
}

Binary file not shown.

View File

@@ -1,162 +0,0 @@
// armv6-m upkr unpacker by yrlf
// some optimizations by exoticorn
.syntax unified
.thumb
.section .text
#define ALIGNUP(n, align) (((n) + (align) - 1) & ~((align) - 1))
#define PROB_LEN (1 + 255 + 1 + 2*32 + 2*32)
#define FRAME_SIZE ALIGNUP(PROB_LEN, 4)
// auto upkr_unpack(uint8_t * out, uint8_t * in) -> tuple<uint8_t *, uint8_t *>
.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 .. subroutine arg (preserved)
// r6 .. decode_bit ret
// r7 .. probs ptr
upkr_unpack:
push { r4, r5, r6, r7, lr }
sub sp, sp, #FRAME_SIZE
mov r7, sp
movs r2, #255
adds r2, r2, #(PROB_LEN - 255)
movs r3, #128
.Lclear:
subs r2, r2, #1
strb r3, [r7, r2]
bne .Lclear
.Lloop:
movs r5, #0
bl upkr_decode_bit
beq .Ldata
.Lmatch:
// r6 = 1
lsls r5, r6, #8
cmp r4, #0
beq 1f
bl upkr_decode_bit
beq 2f
1:
bl upkr_decode_length
adds r3, r4, #1
beq .Lend
2:
adds r5, r5, #64
bl upkr_decode_length
.Lcopy_loop:
ldrb r5, [r0, r3]
.Lstore:
strb r5, [r0]
adds r0, r0, #1
adds r4, r4, #1
blt .Lcopy_loop
b .Lloop
.Ldata:
movs r5, #1
.Ldata_loop:
bl upkr_decode_bit
adcs r5, r5, r5
lsrs r4, r5, #8
beq .Ldata_loop
b .Lstore
.Lend:
add sp, sp, #FRAME_SIZE
pop { r4, r5, r6, r7, pc }
.type upkr_decode_length, %function
// r0 .. -length tmp (saved)
// r1 ..
// r2 ..
// r3 ..
// r4 .. -length (returned)
// r5 .. context index (saved)
// r6 .. (saved)
// r7 ..
upkr_decode_length:
push { r0, r5, r6, lr }
movs r0, #0
subs r4, r0, #1
.Lbit_loop:
adds r5, r5, #1
bl upkr_decode_bit
beq 1f
adds r5, r5, #1
bl upkr_decode_bit
beq 2f
adds r0, r0, r4
2:
lsls r4, r4, #1
b .Lbit_loop
1:
adds r4, r4, r0
pop { r0, r5, r6, pc }
.type upkr_decode_bit, %function
// r0 .. tmp / prob (saved)
// r1 .. in_ptr (modified)
// r2 .. state (modified)
// r3 .. scratch (saved)
// r4 ..
// r5 .. context index (preserved)
// r6 .. bit (returned)
// r7 .. probs ptr (preserved)
upkr_fill_state:
lsls r2, r2, #8
ldrb r6, [r1]
adds r1, r1, #1
orrs r2, r2, r6
upkr_decode_bit:
lsrs r6, r2, #12
beq upkr_fill_state
push { r0, r1, r3, lr }
ldrb r0, [r7, r5]
lsrs r3, r2, #8
uxtb r1, r2
subs r6, r1, r0
blt 1f
subs r1, r2, r0
rsbs r0, r0, #0
1:
muls r3, r3, r0
adds r2, r1, r3
rsbs r3, r0, #0
uxtb r3, r3
lsrs r3, r3, #4
adcs r0, r0, r3
cmp r6, #0
blt 1f
rsbs r0, r0, #0
1:
strb r0, [r7, r5]
lsrs r6, r6, #31
pop { r0, r1, r3, pc }

View File

@@ -1,144 +0,0 @@
.section .text
#define FRAME_SIZE (256+64*4+4)
// x8 prob array ptr
// x9 prev was literal
// x10 out ptr
// x11 in ptr
// x12 offset
// x13 state
.global upkr_unpack
.type upkr_unpack, %function
upkr_unpack:
mv t4, ra
mv x17, x8
mv t6, x9
li x13, FRAME_SIZE
li x9, 128
1:
addi sp, sp, -1
sb x9, 0(sp)
addi x13, x13, -1
bnez x13, 1b
.Lmainloop:
li x14, 0
jal upkr_decode_bit
beqz x15, .Lliteral
li x14, 256
beqz x9, .Lread_offset
jal upkr_decode_bit
beqz x15, .Lskip_offset
.Lread_offset:
jal t3, upkr_decode_number
addi x12, x9, -1
beqz x12, .Ldone
.Lskip_offset:
li x14, 256+64
jal t3, upkr_decode_number
1:
sub x15, x10, x12
lbu x15, (x15)
sb x15, (x10)
addi x10, x10, 1
addi x9, x9, -1
bnez x9, 1b
j .Lmainloop
.Lliteral:
li x14, 1
1:
jal upkr_decode_bit
slli x14, x14, 1
add x14, x14, x15
srli x9, x14, 8
beqz x9, 1b
sb x14, 0(x10)
addi x10, x10, 1
j .Lmainloop
.Ldone:
addi sp, sp, FRAME_SIZE
mv x8, x17
mv x9, t6
jr t4
// x14 context index
// return: x9 decoded number
upkr_decode_number:
mv t5, x14
li x9, 0
li x8, 1
1:
addi x14, x14, 1
jal upkr_decode_bit
beqz x15, 1f
addi x14, x14, 1
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:
lbu x15, 0(x11)
addi x11, x11, 1
slli x13, x13, 8
add x13, x13, x15
// x8 prob array ptr
// x11 in ptr
// x13 state
// x14 context index
// return: x15 decoded bit
upkr_decode_bit:
srli x15, x13, 12
beqz x15, upkr_load_byte
mv t0, x9
mv t1, x14
mv t2, x10
add x14, x14, sp
lbu x9, 0(x14)
andi x10, x13, 255
sltu x15, x10, x9
srli x13, x13, 8
beqz x15, .Lelse
mul x13, x13, x9
add x13, x13, x10
li x10, 256 + 8
sub x10, x10, x9
srli x10, x10, 4
add x9, x9, x10
j .Lendif
.Lelse:
li x16, 256
sub x16, x16, x9
mul x13, x13, x16
add x13, x13, x10
sub x13, x13, x9
addi x10, x9, 8
srli x10, x10, 4
sub x9, x9, x10
.Lendif:
sb x9, 0(x14)
mv x9, t0
mv x14, t1
mv x10, t2
ret

View File

@@ -1,5 +0,0 @@
unpack
unpack_bitstream
unpack_debug
*.upk

View File

@@ -1,10 +0,0 @@
all: unpack unpack_bitstream
unpack: main.c unpack.c
cc -O2 -o unpack main.c unpack.c
unpack_bitstream: main.c unpack.c
cc -O2 -D UPKR_BITSTREAM -o unpack_bitstream main.c unpack.c
unpack_debug: main.c unpack.c
cc -g -o unpack_debug main.c unpack.c

View File

@@ -1,33 +0,0 @@
int upkr_decode_bit(int context_index) {
#ifdef UPKR_BITSTREAM
while(upkr_state < 32768) {
if(upkr_bits_left == 0) {
upkr_current_byte = *upkr_data_ptr++;
upkr_bits_left = 8;
}
upkr_state = (upkr_state << 1) + (upkr_current_byte & 1);
upkr_current_byte >>= 1;
--upkr_bits_left;
}
#else
while(upkr_state < 4096) {
upkr_state = (upkr_state << 8) | *upkr_data_ptr++;
}
#endif
int prob = upkr_probs[context_index];
int bit = (upkr_state & 255) < prob ? 1 : 0;
if(bit) {
prob = 256 - prob;
}
upkr_state -= prob * ((upkr_state >> 8) + (bit ^ 1));
prob -= (prob + 8) >> 4;
if(bit) {
prob = -prob;
}
upkr_probs[context_index] = prob;
return bit;
}

View File

@@ -1,26 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
void* upkr_unpack(void* destination, void* compressed_data);
int main(int argn, char** argv) {
void* input_buffer = malloc(1024*1024);
void* output_buffer = malloc(1024*1024);
FILE* in_file = fopen(argv[1], "rb");
int in_size = fread(input_buffer, 1, 1024*1024, in_file);
fclose(in_file);
printf("Compressed size: %d\n", in_size);
void* end_ptr = upkr_unpack(output_buffer, input_buffer);
int out_size = (char*)end_ptr - (char*)output_buffer;
printf("Uncompressed size: %d\n", out_size);
FILE* out_file = fopen(argv[2], "wb");
fwrite(output_buffer, 1, out_size, out_file);
fclose(out_file);
return 0;
}

View File

@@ -1,4 +0,0 @@
a very simple unpacker in c, as a reference for people wanting to implement their own unpacker.
absolutely not production ready, it makes no effort to ensure the output buffer can actually
hold the uncompressed data.
!!! Never run on untrusted input !!!

View File

@@ -1,96 +0,0 @@
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
u8* upkr_data_ptr;
u8 upkr_probs[1 + 255 + 1 + 2*32 + 2*32];
#ifdef UPKR_BITSTREAM
u16 upkr_state;
u8 upkr_current_byte;
int upkr_bits_left;
#else
u32 upkr_state;
#endif
int upkr_decode_bit(int context_index) {
#ifdef UPKR_BITSTREAM
while(upkr_state < 32768) {
if(upkr_bits_left == 0) {
upkr_current_byte = *upkr_data_ptr++;
upkr_bits_left = 8;
}
upkr_state = (upkr_state << 1) + (upkr_current_byte & 1);
upkr_current_byte >>= 1;
--upkr_bits_left;
}
#else
while(upkr_state < 4096) {
upkr_state = (upkr_state << 8) | *upkr_data_ptr++;
}
#endif
int prob = upkr_probs[context_index];
int bit = (upkr_state & 255) < prob ? 1 : 0;
if(bit) {
upkr_state = prob * (upkr_state >> 8) + (upkr_state & 255);
prob += (256 - prob + 8) >> 4;
} else {
upkr_state = (256 - prob) * (upkr_state >> 8) + (upkr_state & 255) - prob;
prob -= (prob + 8) >> 4;
}
upkr_probs[context_index] = prob;
return bit;
}
int upkr_decode_length(int context_index) {
int length = 0;
int bit_pos = 0;
while(upkr_decode_bit(context_index)) {
length |= upkr_decode_bit(context_index + 1) << bit_pos++;
context_index += 2;
}
return length | (1 << bit_pos);
}
void* upkr_unpack(void* destination, void* compressed_data) {
upkr_data_ptr = (u8*)compressed_data;
upkr_state = 0;
#ifdef UPKR_BITSTREAM
upkr_bits_left = 0;
#endif
for(int i = 0; i < sizeof(upkr_probs); ++i)
upkr_probs[i] = 128;
u8* write_ptr = (u8*)destination;
int prev_was_match = 0;
int offset = 0;
for(;;) {
if(upkr_decode_bit(0)) {
if(prev_was_match || upkr_decode_bit(256)) {
offset = upkr_decode_length(257) - 1;
if(offset == 0) {
break;
}
}
int length = upkr_decode_length(257 + 64);
while(length--) {
*write_ptr = write_ptr[-offset];
++write_ptr;
}
prev_was_match = 1;
} else {
int byte = 1;
while(byte < 256) {
int bit = upkr_decode_bit(byte);
byte = (byte << 1) + bit;
}
*write_ptr++ = byte;
prev_was_match = 0;
}
}
return write_ptr;
}

View File

@@ -6,12 +6,11 @@ use crate::ProgressCallback;
pub fn pack( pub fn pack(
data: &[u8], data: &[u8],
use_bitstream: bool, use_bitstream: bool,
parity_contexts: usize,
mut progress_callback: Option<ProgressCallback>, mut progress_callback: Option<ProgressCallback>,
) -> Vec<u8> { ) -> Vec<u8> {
let mut match_finder = MatchFinder::new(data); let mut match_finder = MatchFinder::new(data);
let mut rans_coder = RansCoder::new(use_bitstream); let mut rans_coder = RansCoder::new(use_bitstream);
let mut state = lz::CoderState::new(parity_contexts); let mut state = lz::CoderState::new();
let mut pos = 0; let mut pos = 0;
while pos < data.len() { while pos < data.len() {

View File

@@ -9,49 +9,15 @@ pub use lz::unpack;
pub type ProgressCallback<'a> = &'a mut dyn FnMut(usize); pub type ProgressCallback<'a> = &'a mut dyn FnMut(usize);
pub struct Config {
pub use_bitstream: bool,
pub parity_contexts: usize,
}
impl Default for Config {
fn default() -> Config {
Config {
use_bitstream: false,
parity_contexts: 1,
}
}
}
pub fn pack( pub fn pack(
data: &[u8], data: &[u8],
level: u8, level: u8,
config: Config, use_bitstream: bool,
progress_callback: Option<ProgressCallback>, progress_callback: Option<ProgressCallback>,
) -> Vec<u8> { ) -> Vec<u8> {
if level == 0 { if level == 0 {
greedy_packer::pack( greedy_packer::pack(data, use_bitstream, progress_callback)
data,
config.use_bitstream,
config.parity_contexts,
progress_callback,
)
} else { } else {
parsing_packer::pack( parsing_packer::pack(data, level, use_bitstream, progress_callback)
data,
level,
config.use_bitstream,
config.parity_contexts,
progress_callback,
)
} }
} }
pub fn compressed_size(mut data: &[u8]) -> f32 {
let mut state = 0;
while state < 4096 {
state = (state << 8) | data[0] as u32;
data = &data[1..];
}
data.len() as f32 + (state as f32).log2() / 8.
}

View File

@@ -9,49 +9,44 @@ pub enum Op {
impl Op { impl Op {
pub fn encode(&self, coder: &mut dyn EntropyCoder, state: &mut CoderState) { pub fn encode(&self, coder: &mut dyn EntropyCoder, state: &mut CoderState) {
let literal_base = state.pos % state.parity_contexts * 256; let base_context = 256 * (state.pos & 3);
match self { match self {
&Op::Literal(lit) => { &Op::Literal(lit) => {
encode_bit(coder, state, literal_base, false); encode_bit(coder, state, base_context, false);
let mut context_index = 1; let mut context_index = 1;
for i in (0..8).rev() { for i in (0..8).rev() {
let bit = (lit >> i) & 1 != 0; let bit = (lit >> i) & 1 != 0;
encode_bit(coder, state, literal_base + context_index, bit); encode_bit(coder, state, base_context + context_index, bit);
context_index = (context_index << 1) | bit as usize; context_index = (context_index << 1) | bit as usize;
} }
state.prev_was_match = false;
state.pos += 1; state.pos += 1;
state.prev_was_match = false;
} }
&Op::Match { offset, len } => { &Op::Match { offset, len } => {
encode_bit(coder, state, literal_base, true); encode_bit(coder, state, base_context, true);
if !state.prev_was_match { if !state.prev_was_match {
encode_bit( encode_bit(coder, state, 1024, offset != state.last_offset);
coder,
state,
256 * state.parity_contexts,
offset != state.last_offset,
);
} else { } else {
assert!(offset != state.last_offset); assert!(offset != state.last_offset);
} }
if offset != state.last_offset { if offset != state.last_offset {
encode_length(coder, state, 256 * state.parity_contexts + 1, offset + 1); encode_length(coder, state, 1025, offset + 1);
state.last_offset = offset; state.last_offset = offset;
} }
encode_length(coder, state, 256 * state.parity_contexts + 65, len); encode_length(coder, state, 1025 + 64, len);
state.prev_was_match = true;
state.pos += len as usize; state.pos += len as usize;
state.prev_was_match = true;
} }
} }
} }
} }
pub fn encode_eof(coder: &mut dyn EntropyCoder, state: &mut CoderState) { pub fn encode_eof(coder: &mut dyn EntropyCoder, state: &mut CoderState) {
encode_bit(coder, state, state.pos % state.parity_contexts * 256, true); encode_bit(coder, state, 256 * (state.pos & 3), true);
if !state.prev_was_match { if !state.prev_was_match {
encode_bit(coder, state, 256 * state.parity_contexts, true); encode_bit(coder, state, 1024, true);
} }
encode_length(coder, state, 256 * state.parity_contexts + 1, 1); encode_length(coder, state, 1025, 1);
} }
fn encode_bit( fn encode_bit(
@@ -84,20 +79,18 @@ fn encode_length(
#[derive(Clone)] #[derive(Clone)]
pub struct CoderState { pub struct CoderState {
contexts: ContextState, contexts: ContextState,
parity_contexts: usize,
last_offset: u32, last_offset: u32,
prev_was_match: bool,
pos: usize, pos: usize,
prev_was_match: bool,
} }
impl CoderState { impl CoderState {
pub fn new(parity_contexts: usize) -> CoderState { pub fn new() -> CoderState {
CoderState { CoderState {
contexts: ContextState::new((1 + 255) * parity_contexts + 1 + 64 + 64), contexts: ContextState::new((1 + 255) * 4 + 1 + 64 + 64),
last_offset: 0, last_offset: 0,
parity_contexts,
prev_was_match: false,
pos: 0, pos: 0,
prev_was_match: false,
} }
} }
@@ -106,9 +99,9 @@ impl CoderState {
} }
} }
pub fn unpack(packed_data: &[u8], config: crate::Config) -> Vec<u8> { pub fn unpack(packed_data: &[u8], use_bitstream: bool) -> Vec<u8> {
let mut decoder = RansDecoder::new(packed_data, config.use_bitstream); let mut decoder = RansDecoder::new(packed_data, use_bitstream);
let mut contexts = ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64); let mut contexts = ContextState::new((1 + 255) * 4 + 1 + 64 + 64);
let mut result = vec![]; let mut result = vec![];
let mut offset = 0; let mut offset = 0;
let mut prev_was_match = false; let mut prev_was_match = false;
@@ -131,26 +124,15 @@ pub fn unpack(packed_data: &[u8], config: crate::Config) -> Vec<u8> {
} }
loop { loop {
let literal_base = result.len() % config.parity_contexts * 256; let base_context = 256 * (result.len() & 3);
if decoder.decode_with_context(&mut contexts.context_mut(literal_base)) { if decoder.decode_with_context(&mut contexts.context_mut(base_context)) {
if prev_was_match if prev_was_match || decoder.decode_with_context(&mut contexts.context_mut(1024)) {
|| decoder offset = decode_length(&mut decoder, &mut contexts, 1025) - 1;
.decode_with_context(&mut contexts.context_mut(256 * config.parity_contexts))
{
offset = decode_length(
&mut decoder,
&mut contexts,
256 * config.parity_contexts + 1,
) - 1;
if offset == 0 { if offset == 0 {
break; break;
} }
} }
let length = decode_length( let length = decode_length(&mut decoder, &mut contexts, 1025 + 64);
&mut decoder,
&mut contexts,
256 * config.parity_contexts + 65,
);
for _ in 0..length { for _ in 0..length {
result.push(result[result.len() - offset]); result.push(result[result.len() - offset]);
} }
@@ -159,8 +141,7 @@ pub fn unpack(packed_data: &[u8], config: crate::Config) -> Vec<u8> {
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() {
let bit = decoder let bit = decoder.decode_with_context(&mut contexts.context_mut(base_context + context_index));
.decode_with_context(&mut contexts.context_mut(literal_base + context_index));
context_index = (context_index << 1) | bit as usize; context_index = (context_index << 1) | bit as usize;
byte |= (bit as u8) << i; byte |= (bit as u8) << i;
} }

View File

@@ -1,81 +1,34 @@
use anyhow::Result; use anyhow::{bail, Result};
use std::ffi::OsStr;
use std::io::prelude::*; use std::io::prelude::*;
use std::process;
use std::{fs::File, path::PathBuf}; use std::{fs::File, path::PathBuf};
fn main() -> Result<()> { fn main() -> Result<()> {
let mut config = upkr::Config::default(); let mut args = pico_args::Arguments::from_env();
let mut reverse = false;
let mut unpack = false;
let mut level = 2;
let mut infile: Option<PathBuf> = None;
let mut outfile: Option<PathBuf> = None;
let mut parser = lexopt::Parser::from_env(); match args.subcommand()?.as_ref().map(|s| s.as_str()) {
while let Some(arg) = parser.next()? { None => print_help(),
use lexopt::prelude::*; Some("pack") => {
match arg { let level = args.opt_value_from_str(["-l", "--level"])?.unwrap_or(2u8);
Short('b') | Long("bitstream") => config.use_bitstream = true, let use_bitstream = args.contains(["-b", "--bitstream"]);
Short('p') | Long("parity") => config.parity_contexts = parser.value()?.parse()?,
Short('r') | Long("reverse") => reverse = true,
Short('u') | Long("unpack") => unpack = true,
Short('l') | Long("level") => level = parser.value()?.parse()?,
Short('h') | Long("help") => print_help(0),
Value(val) if infile.is_none() => infile = Some(val.try_into()?),
Value(val) if outfile.is_none() => outfile = Some(val.try_into()?),
_ => return Err(arg.unexpected().into()),
}
}
let infile = infile.unwrap_or_else(|| print_help(1)); let infile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
let outfile = outfile.unwrap_or_else(|| { let outfile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
let mut name = infile.clone();
if unpack {
if name.extension().filter(|&e| e == "upk").is_some() {
name.set_extension("");
} else {
name.set_extension("bin");
}
} else {
let mut filename = name
.file_name()
.unwrap_or_else(|| OsStr::new(""))
.to_os_string();
filename.push(".upk");
name.set_file_name(filename);
}
name
});
if config.parity_contexts != 1 && config.parity_contexts != 2 && config.parity_contexts != 4 {
eprintln!("--parity has to be 1, 2, or 4");
process::exit(1);
}
if !unpack {
let mut data = vec![]; let mut data = vec![];
File::open(infile)?.read_to_end(&mut data)?; File::open(infile)?.read_to_end(&mut data)?;
if reverse {
data.reverse();
}
let mut pb = pbr::ProgressBar::new(data.len() as u64); let mut pb = pbr::ProgressBar::new(data.len() as u64);
pb.set_units(pbr::Units::Bytes); pb.set_units(pbr::Units::Bytes);
let mut packed_data = upkr::pack( let packed_data = upkr::pack(
&data, &data,
level, level,
config, use_bitstream,
Some(&mut |pos| { Some(&mut |pos| {
pb.set(pos as u64); pb.set(pos as u64);
}), }),
); );
pb.finish(); pb.finish();
if reverse {
packed_data.reverse();
}
println!( println!(
"Compressed {} bytes to {} bytes ({}%)", "Compressed {} bytes to {} bytes ({}%)",
data.len(), data.len(),
@@ -83,33 +36,29 @@ fn main() -> Result<()> {
packed_data.len() as f32 * 100. / data.len() as f32 packed_data.len() as f32 * 100. / data.len() as f32
); );
File::create(outfile)?.write_all(&packed_data)?; File::create(outfile)?.write_all(&packed_data)?;
} else { }
Some("unpack") => {
let use_bitstream = args.contains(["-b", "--bitstream"]);
let infile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
let outfile = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
let mut data = vec![]; let mut data = vec![];
File::open(infile)?.read_to_end(&mut data)?; File::open(infile)?.read_to_end(&mut data)?;
if reverse { let packed_data = upkr::unpack(&data, use_bitstream);
data.reverse(); File::create(outfile)?.write_all(&packed_data)?;
} }
let mut unpacked_data = upkr::unpack(&data, config); Some(other) => {
if reverse { bail!("Unknown subcommand '{}'", other);
unpacked_data.reverse();
} }
File::create(outfile)?.write_all(&unpacked_data)?;
} }
Ok(()) Ok(())
} }
fn print_help(exit_code: i32) -> ! { fn print_help() {
eprintln!("Usage:"); eprintln!("Usage:");
eprintln!(" upkr [-l level(0-9)] [config options] <infile> [<outfile>]"); eprintln!(" upkr pack [-l level(0-9)] <infile> <outfile>");
eprintln!(" upkr -u [config options] <infile> [<outfile>]"); eprintln!(" upkr unpack <infile> <outfile>");
eprintln!(); std::process::exit(1);
eprintln!(" -l, --level N compression level 0-9");
eprintln!(" -u, --unpack unpack infile");
eprintln!();
eprintln!("Config options (need to match when packing/unpacking):");
eprintln!(" -b, --bitstream bitstream mode");
eprintln!(" -p, --parity N use N (2/4) parity contexts");
eprintln!(" -r, --reverse reverse input & output");
process::exit(exit_code);
} }

View File

@@ -6,25 +6,14 @@ use crate::match_finder::MatchFinder;
use crate::rans::{CostCounter, RansCoder}; use crate::rans::{CostCounter, RansCoder};
use crate::{lz, ProgressCallback}; use crate::{lz, ProgressCallback};
pub fn pack( pub fn pack(data: &[u8], level: u8, use_bitstream: bool, progress_cb: Option<ProgressCallback>) -> Vec<u8> {
data: &[u8], let mut parse = parse(data, Config::from_level(level), progress_cb);
level: u8,
use_bitstream: bool,
parity_contexts: usize,
progress_cb: Option<ProgressCallback>,
) -> Vec<u8> {
let mut parse = parse(
data,
Config::from_level(level),
parity_contexts,
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);
parse = link.prev.clone(); parse = link.prev.clone();
} }
let mut state = lz::CoderState::new(parity_contexts); let mut state = lz::CoderState::new();
let mut coder = RansCoder::new(use_bitstream); let mut coder = RansCoder::new(use_bitstream);
for op in ops.into_iter().rev() { for op in ops.into_iter().rev() {
op.encode(&mut coder, &mut state); op.encode(&mut coder, &mut state);
@@ -49,7 +38,6 @@ type Arrivals = HashMap<usize, Vec<Arrival>>;
fn parse( fn parse(
data: &[u8], data: &[u8],
config: Config, config: Config,
parity_contexts: usize,
mut progress_cb: Option<ProgressCallback>, mut progress_cb: Option<ProgressCallback>,
) -> Option<Rc<Parse>> { ) -> Option<Rc<Parse>> {
let mut match_finder = MatchFinder::new(data) let mut match_finder = MatchFinder::new(data)
@@ -141,7 +129,7 @@ fn parse(
0, 0,
Arrival { Arrival {
parse: None, parse: None,
state: lz::CoderState::new(parity_contexts), state: lz::CoderState::new(),
cost: 0.0, cost: 0.0,
}, },
max_arrivals, max_arrivals,