From 869fe69fdf947230ca4129d3d1f9749360035e64 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 18 Apr 2021 15:20:13 +0530 Subject: add RLE compression functions --- .direnv/flake-profile | 2 +- .direnv/flake-profile.rc | 20 ++++++----- src/decode.rs | 69 ++++++++++++++++++++++++++++--------- src/encode.rs | 44 ++++++++++++++++++------ src/lib.rs | 16 ++++++++- src/rle.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/serde.rs | 22 ++++++++++-- 7 files changed, 224 insertions(+), 38 deletions(-) create mode 100644 src/rle.rs diff --git a/.direnv/flake-profile b/.direnv/flake-profile index 624f00f..0e6e742 120000 --- a/.direnv/flake-profile +++ b/.direnv/flake-profile @@ -1 +1 @@ -/nix/store/hk8227smba1l2lrk4np5fqy2hkb0jaf3-nix-shell-env \ No newline at end of file +/nix/store/q3bk2x9q1g2xnah8j9l9xpl2037ks0xv-nix-shell-env \ No newline at end of file diff --git a/.direnv/flake-profile.rc b/.direnv/flake-profile.rc index 49f03d2..3796858 100644 --- a/.direnv/flake-profile.rc +++ b/.direnv/flake-profile.rc @@ -32,7 +32,7 @@ NIX_CC=/nix/store/i9a3zzf2spmw67jppvkj2yavnbka62id-gcc-wrapper-10.2.0 export NIX_CC NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu=1 export NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu -NIX_CFLAGS_COMPILE=' -frandom-seed=hk8227smba -isystem /nix/store/91ym2jzh08470r4jb1d5pmh22vg2bpck-openssl-1.1.1i-dev/include -isystem /nix/store/saiwalr8jq58mbyipkn824r93gjjnw5x-ncurses-6.2-dev/include -isystem /nix/store/91ym2jzh08470r4jb1d5pmh22vg2bpck-openssl-1.1.1i-dev/include -isystem /nix/store/saiwalr8jq58mbyipkn824r93gjjnw5x-ncurses-6.2-dev/include' +NIX_CFLAGS_COMPILE=' -frandom-seed=q3bk2x9q1g' export NIX_CFLAGS_COMPILE NIX_ENFORCE_NO_NATIVE=1 export NIX_ENFORCE_NO_NATIVE @@ -40,7 +40,7 @@ NIX_HARDENING_ENABLE='fortify stackprotector pic strictoverflow format relro bin export NIX_HARDENING_ENABLE NIX_INDENT_MAKE=1 export NIX_INDENT_MAKE -NIX_LDFLAGS='-rpath /home/np/code/rust/obi/outputs/out/lib64 -rpath /home/np/code/rust/obi/outputs/out/lib -L/nix/store/ni0nxz7jnd3scrfqsdr6cragl95fqmyb-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/lib -L/nix/store/kchm26396krqwdn2dkx6l0hb79lwlhr1-openssl-1.1.1i/lib -L/nix/store/d3nz4c5xf1bqz8rwr8m4gbsrny9ffidk-ncurses-6.2/lib -L/nix/store/ni0nxz7jnd3scrfqsdr6cragl95fqmyb-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/lib -L/nix/store/kchm26396krqwdn2dkx6l0hb79lwlhr1-openssl-1.1.1i/lib -L/nix/store/d3nz4c5xf1bqz8rwr8m4gbsrny9ffidk-ncurses-6.2/lib' +NIX_LDFLAGS='-rpath /home/np/code/rust/obi/outputs/out/lib64 -rpath /home/np/code/rust/obi/outputs/out/lib -L/nix/store/58syiz9158g5s53gjh8f5b89bhhp1m0s-rust-1.53.0-nightly-2021-03-30-74874a690/lib -L/nix/store/58syiz9158g5s53gjh8f5b89bhhp1m0s-rust-1.53.0-nightly-2021-03-30-74874a690/lib' export NIX_LDFLAGS NIX_LIB64_IN_SELF_RPATH=1 NIX_NO_SELF_RPATH=1 @@ -55,7 +55,7 @@ export OBJDUMP OPTERR=1 OPTIND=1 OSTYPE=linux-gnu -PATH=/nix/store/ni0nxz7jnd3scrfqsdr6cragl95fqmyb-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/bin:/nix/store/pj3m562kh9lr7wjk3hlyandhd1qpapgh-rust-analyzer-unstable-2021-02-22/bin:/nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0/bin:/nix/store/71707dbkha4mryph7ywp48wi2r1sc0hf-openssl-1.1.1i-bin/bin:/nix/store/saiwalr8jq58mbyipkn824r93gjjnw5x-ncurses-6.2-dev/bin:/nix/store/d3nz4c5xf1bqz8rwr8m4gbsrny9ffidk-ncurses-6.2/bin:/nix/store/d7lyvmvzf1a8qqhsxhdc3r6dwbdmfcap-patchelf-0.12/bin:/nix/store/i9a3zzf2spmw67jppvkj2yavnbka62id-gcc-wrapper-10.2.0/bin:/nix/store/wxri3dc14yd1w997vjfvsnn6qml64l4r-gcc-10.2.0/bin:/nix/store/fr2k1vwmyd5ngmb3y0v090nh21nn6gqp-glibc-2.32-35-bin/bin:/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32/bin:/nix/store/f4w8p656gg6v2k1v0whwmyk09kz3k8ic-binutils-wrapper-2.35.1/bin:/nix/store/9f8y44vmjnwdjvzlff0gm3f3g6ycyyqy-binutils-2.35.1/bin:/nix/store/fr2k1vwmyd5ngmb3y0v090nh21nn6gqp-glibc-2.32-35-bin/bin:/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32/bin:/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32/bin:/nix/store/nq9l23mrm8irvl9qvxgvxqqkm5sfnc9d-findutils-4.7.0/bin:/nix/store/7yq29nnglh2s576n7vdmzqryym22f8rj-diffutils-3.7/bin:/nix/store/0839nqri8w69c91amlsq9bawaqlzpmh9-gnused-4.8/bin:/nix/store/wmiyjdsaydyv024al5ddqd3liljrfvk7-gnugrep-3.6/bin:/nix/store/snmar11l5w9jrxva2s5d6143nqmdgzqb-gawk-5.1.0/bin:/nix/store/cisss0pcfh72kmnsv4qa6h41wcwb9mkv-gnutar-1.32/bin:/nix/store/prdifiyrxwgf6sp4rwmrqvfdnvf72gw2-gzip-1.10/bin:/nix/store/pp9shwhcdxxbhgp96fcaj0g8x5g54zrl-bzip2-1.0.6.0.1-bin/bin:/nix/store/fydw5vhlhpq438cxv4vwpa8pf8p8fv5s-gnumake-4.3/bin:/nix/store/yyy7wr7r9jwjjqkr1yn643g3wzv010zd-bash-4.4-p23/bin:/nix/store/qcw3qagp709qwfza259zqss5qh4wg789-patch-2.7.6/bin:/nix/store/q0jqhjabwaaak30l4sq3z0xi6jrkpnbn-xz-5.2.5-bin/bin +PATH=/nix/store/58syiz9158g5s53gjh8f5b89bhhp1m0s-rust-1.53.0-nightly-2021-03-30-74874a690/bin:/nix/store/pj3m562kh9lr7wjk3hlyandhd1qpapgh-rust-analyzer-unstable-2021-02-22/bin:/nix/store/rfx0jz6pr3n2dcxm99finhbp99v45qx4-rustfmt-1.49.0/bin:/nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0/bin:/nix/store/d7lyvmvzf1a8qqhsxhdc3r6dwbdmfcap-patchelf-0.12/bin:/nix/store/i9a3zzf2spmw67jppvkj2yavnbka62id-gcc-wrapper-10.2.0/bin:/nix/store/wxri3dc14yd1w997vjfvsnn6qml64l4r-gcc-10.2.0/bin:/nix/store/fr2k1vwmyd5ngmb3y0v090nh21nn6gqp-glibc-2.32-35-bin/bin:/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32/bin:/nix/store/f4w8p656gg6v2k1v0whwmyk09kz3k8ic-binutils-wrapper-2.35.1/bin:/nix/store/9f8y44vmjnwdjvzlff0gm3f3g6ycyyqy-binutils-2.35.1/bin:/nix/store/fr2k1vwmyd5ngmb3y0v090nh21nn6gqp-glibc-2.32-35-bin/bin:/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32/bin:/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32/bin:/nix/store/nq9l23mrm8irvl9qvxgvxqqkm5sfnc9d-findutils-4.7.0/bin:/nix/store/7yq29nnglh2s576n7vdmzqryym22f8rj-diffutils-3.7/bin:/nix/store/0839nqri8w69c91amlsq9bawaqlzpmh9-gnused-4.8/bin:/nix/store/wmiyjdsaydyv024al5ddqd3liljrfvk7-gnugrep-3.6/bin:/nix/store/snmar11l5w9jrxva2s5d6143nqmdgzqb-gawk-5.1.0/bin:/nix/store/cisss0pcfh72kmnsv4qa6h41wcwb9mkv-gnutar-1.32/bin:/nix/store/prdifiyrxwgf6sp4rwmrqvfdnvf72gw2-gzip-1.10/bin:/nix/store/pp9shwhcdxxbhgp96fcaj0g8x5g54zrl-bzip2-1.0.6.0.1-bin/bin:/nix/store/fydw5vhlhpq438cxv4vwpa8pf8p8fv5s-gnumake-4.3/bin:/nix/store/yyy7wr7r9jwjjqkr1yn643g3wzv010zd-bash-4.4-p23/bin:/nix/store/qcw3qagp709qwfza259zqss5qh4wg789-patch-2.7.6/bin:/nix/store/q0jqhjabwaaak30l4sq3z0xi6jrkpnbn-xz-5.2.5-bin/bin export PATH PIPESTATUS=([0]="0") PS4='+ ' @@ -63,7 +63,11 @@ RANLIB=ranlib export RANLIB READELF=readelf export READELF -RUST_SRC_PATH=/nix/store/ncxadjgpdacbf69yhpgwvwnqhmh5yk96-rust-lib-src +RUST_BACKTRACE=1 +export RUST_BACKTRACE +RUST_LOG=info +export RUST_LOG +RUST_SRC_PATH=/nix/store/m0k4il8v2wv0c7nniqndcxh72qmzvvg2-rust-src-1.53.0-nightly-2021-03-30-74874a690/lib/rustlib/src/rust/library export RUST_SRC_PATH SHELL=/nix/store/yyy7wr7r9jwjjqkr1yn643g3wzv010zd-bash-4.4-p23/bin/bash export SHELL @@ -75,7 +79,7 @@ STRINGS=strings export STRINGS STRIP=strip export STRIP -XDG_DATA_DIRS=/nix/store/ni0nxz7jnd3scrfqsdr6cragl95fqmyb-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/share:/nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0/share:/nix/store/d3nz4c5xf1bqz8rwr8m4gbsrny9ffidk-ncurses-6.2/share:/nix/store/d7lyvmvzf1a8qqhsxhdc3r6dwbdmfcap-patchelf-0.12/share +XDG_DATA_DIRS=/nix/store/58syiz9158g5s53gjh8f5b89bhhp1m0s-rust-1.53.0-nightly-2021-03-30-74874a690/share:/nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0/share:/nix/store/d7lyvmvzf1a8qqhsxhdc3r6dwbdmfcap-patchelf-0.12/share export XDG_DATA_DIRS _=export buildInputs= @@ -115,7 +119,7 @@ fixupOutputHooks=([0]="if [ -z \"\${dontPatchELF-}\" ]; then patchELF \"\$prefix initialPath='/nix/store/ypsd29c5hgj1x7xz5ddffanxw5d8fh7b-coreutils-8.32 /nix/store/nq9l23mrm8irvl9qvxgvxqqkm5sfnc9d-findutils-4.7.0 /nix/store/7yq29nnglh2s576n7vdmzqryym22f8rj-diffutils-3.7 /nix/store/0839nqri8w69c91amlsq9bawaqlzpmh9-gnused-4.8 /nix/store/wmiyjdsaydyv024al5ddqd3liljrfvk7-gnugrep-3.6 /nix/store/snmar11l5w9jrxva2s5d6143nqmdgzqb-gawk-5.1.0 /nix/store/cisss0pcfh72kmnsv4qa6h41wcwb9mkv-gnutar-1.32 /nix/store/prdifiyrxwgf6sp4rwmrqvfdnvf72gw2-gzip-1.10 /nix/store/pp9shwhcdxxbhgp96fcaj0g8x5g54zrl-bzip2-1.0.6.0.1-bin /nix/store/fydw5vhlhpq438cxv4vwpa8pf8p8fv5s-gnumake-4.3 /nix/store/yyy7wr7r9jwjjqkr1yn643g3wzv010zd-bash-4.4-p23 /nix/store/qcw3qagp709qwfza259zqss5qh4wg789-patch-2.7.6 /nix/store/q0jqhjabwaaak30l4sq3z0xi6jrkpnbn-xz-5.2.5-bin' name=nix-shell export name -nativeBuildInputs='/nix/store/ni0nxz7jnd3scrfqsdr6cragl95fqmyb-rust-1.50.0-nightly-2020-12-22-bb1fbbf84 /nix/store/pgzjwsl6ilg93ifsx70ck5l2yv85fknm-rust-src-1.50.0-nightly-2020-12-22-bb1fbbf84 /nix/store/pj3m562kh9lr7wjk3hlyandhd1qpapgh-rust-analyzer-unstable-2021-02-22 /nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0 /nix/store/91ym2jzh08470r4jb1d5pmh22vg2bpck-openssl-1.1.1i-dev /nix/store/saiwalr8jq58mbyipkn824r93gjjnw5x-ncurses-6.2-dev' +nativeBuildInputs='/nix/store/58syiz9158g5s53gjh8f5b89bhhp1m0s-rust-1.53.0-nightly-2021-03-30-74874a690 /nix/store/m0k4il8v2wv0c7nniqndcxh72qmzvvg2-rust-src-1.53.0-nightly-2021-03-30-74874a690 /nix/store/pj3m562kh9lr7wjk3hlyandhd1qpapgh-rust-analyzer-unstable-2021-02-22 /nix/store/rfx0jz6pr3n2dcxm99finhbp99v45qx4-rustfmt-1.49.0 /nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0' export nativeBuildInputs nobuildPhase=$'echo\necho "This derivation is not meant to be built, aborting";\necho\nexit 1\n' export nobuildPhase @@ -137,7 +141,7 @@ export patches phases=nobuildPhase export phases pkg=/nix/store/i9a3zzf2spmw67jppvkj2yavnbka62id-gcc-wrapper-10.2.0 -pkgsBuildHost=([0]="/nix/store/ni0nxz7jnd3scrfqsdr6cragl95fqmyb-rust-1.50.0-nightly-2020-12-22-bb1fbbf84" [1]="/nix/store/pgzjwsl6ilg93ifsx70ck5l2yv85fknm-rust-src-1.50.0-nightly-2020-12-22-bb1fbbf84" [2]="/nix/store/pj3m562kh9lr7wjk3hlyandhd1qpapgh-rust-analyzer-unstable-2021-02-22" [3]="/nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0" [4]="/nix/store/91ym2jzh08470r4jb1d5pmh22vg2bpck-openssl-1.1.1i-dev" [5]="/nix/store/71707dbkha4mryph7ywp48wi2r1sc0hf-openssl-1.1.1i-bin" [6]="/nix/store/kchm26396krqwdn2dkx6l0hb79lwlhr1-openssl-1.1.1i" [7]="/nix/store/saiwalr8jq58mbyipkn824r93gjjnw5x-ncurses-6.2-dev" [8]="/nix/store/d3nz4c5xf1bqz8rwr8m4gbsrny9ffidk-ncurses-6.2" [9]="/nix/store/d7lyvmvzf1a8qqhsxhdc3r6dwbdmfcap-patchelf-0.12" [10]="/nix/store/mjjy30kxz775bhhi6j9phw81qh6dsbrf-move-docs.sh" [11]="/nix/store/kxw6q8v6isaqjm702d71n2421cxamq68-make-symlinks-relative.sh" [12]="/nix/store/cl3qd985p1yxyfkj96v0hqxiy3w69xq5-compress-man-pages.sh" [13]="/nix/store/h54dzwd7rdh2jlcv91424csl6d0ccgjy-strip.sh" [14]="/nix/store/bnj8d7mvbkg3vdb07yz74yhl3g107qq5-patch-shebangs.sh" [15]="/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh" [16]="/nix/store/8zxndz5ag0p6s526c2xyllhk1nrn4c3i-audit-tmpdir.sh" [17]="/nix/store/c8n9kcdddp9np665xz6ri61b383nxvz8-move-systemd-user-units.sh" [18]="/nix/store/1i5y55x4b4m9qkx5dqbmr1r6bvrqbanw-multiple-outputs.sh" [19]="/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh" [20]="/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh" [21]="/nix/store/ngg1cv31c8c7bcm2n8ww4g06nq7s4zhm-set-source-date-epoch-to-latest.sh" [22]="/nix/store/pr6nzbscdpg94yvr151lrif2kg0csx7b-reproducible-builds.sh" [23]="/nix/store/i9a3zzf2spmw67jppvkj2yavnbka62id-gcc-wrapper-10.2.0" [24]="/nix/store/f4w8p656gg6v2k1v0whwmyk09kz3k8ic-binutils-wrapper-2.35.1") +pkgsBuildHost=([0]="/nix/store/58syiz9158g5s53gjh8f5b89bhhp1m0s-rust-1.53.0-nightly-2021-03-30-74874a690" [1]="/nix/store/m0k4il8v2wv0c7nniqndcxh72qmzvvg2-rust-src-1.53.0-nightly-2021-03-30-74874a690" [2]="/nix/store/pj3m562kh9lr7wjk3hlyandhd1qpapgh-rust-analyzer-unstable-2021-02-22" [3]="/nix/store/rfx0jz6pr3n2dcxm99finhbp99v45qx4-rustfmt-1.49.0" [4]="/nix/store/9p0yf4yzsc8by6xviags56c3mb3fxkkw-cargo-1.49.0" [5]="/nix/store/d7lyvmvzf1a8qqhsxhdc3r6dwbdmfcap-patchelf-0.12" [6]="/nix/store/mjjy30kxz775bhhi6j9phw81qh6dsbrf-move-docs.sh" [7]="/nix/store/kxw6q8v6isaqjm702d71n2421cxamq68-make-symlinks-relative.sh" [8]="/nix/store/cl3qd985p1yxyfkj96v0hqxiy3w69xq5-compress-man-pages.sh" [9]="/nix/store/h54dzwd7rdh2jlcv91424csl6d0ccgjy-strip.sh" [10]="/nix/store/bnj8d7mvbkg3vdb07yz74yhl3g107qq5-patch-shebangs.sh" [11]="/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh" [12]="/nix/store/8zxndz5ag0p6s526c2xyllhk1nrn4c3i-audit-tmpdir.sh" [13]="/nix/store/c8n9kcdddp9np665xz6ri61b383nxvz8-move-systemd-user-units.sh" [14]="/nix/store/1i5y55x4b4m9qkx5dqbmr1r6bvrqbanw-multiple-outputs.sh" [15]="/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh" [16]="/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh" [17]="/nix/store/ngg1cv31c8c7bcm2n8ww4g06nq7s4zhm-set-source-date-epoch-to-latest.sh" [18]="/nix/store/pr6nzbscdpg94yvr151lrif2kg0csx7b-reproducible-builds.sh" [19]="/nix/store/i9a3zzf2spmw67jppvkj2yavnbka62id-gcc-wrapper-10.2.0" [20]="/nix/store/f4w8p656gg6v2k1v0whwmyk09kz3k8ic-binutils-wrapper-2.35.1") postFixupHooks=([0]="_multioutPropagateDev") postUnpackHooks=([0]="_updateSourceDateEpochFromSourceRoot") preConfigureHooks=([0]="_multioutConfig") @@ -1496,7 +1500,7 @@ updateSourceDateEpoch () fi } -export NIX_BUILD_TOP="$(mktemp -d --tmpdir nix-shell.XXXXXX)" +export NIX_BUILD_TOP="$(mktemp -d -t nix-shell.XXXXXX)" export TMP="$NIX_BUILD_TOP" export TMPDIR="$NIX_BUILD_TOP" export TEMP="$NIX_BUILD_TOP" diff --git a/src/decode.rs b/src/decode.rs index a9abd37..9c6a065 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -4,7 +4,7 @@ use bitvec::prelude::*; use byteorder::{LittleEndian, ReadBytesExt}; use crate::error::{OBIError, OBIResult}; -use crate::{FileHeader, Image, ImageInfoHeader}; +use crate::{CompressionType, FileHeader, Image, ImageInfoHeader}; pub fn decode_image(obi_data: &mut Cursor>) -> OBIResult { // file header @@ -43,22 +43,59 @@ pub fn decode_image(obi_data: &mut Cursor>) -> OBIResult { post_compression_size, }; - // pixmap data - let mut data_bytes = vec![]; - obi_data - .read_to_end(&mut data_bytes) - .map_err(|_| OBIError::Decode)?; - let data = data_bytes - .iter() - .map(|&b| { - BitVec::::from_element(b) - .into_iter() - .map(|e| e as bool) - .collect::>() - }) - .flatten() - .collect::>(); + let data: Vec = match CompressionType::from_u32(compression_type) { + CompressionType::RLE => { + let mut rest = vec![]; + let mut lengths = vec![]; + loop { + let l = obi_data + .read_u32::() + .map_err(|_| OBIError::Encode)?; + if l == 0 { + break; + } + lengths.push(l); + } + obi_data + .read_to_end(&mut rest) + .map_err(|_| OBIError::Decode)?; + let data_points = rest + .iter() + .map(|&b| { + BitVec::::from_element(b) + .into_iter() + .map(|e| e as bool) + .collect::>() + }) + .flatten() + .collect::>(); + let data = data_points + .into_iter() + .zip(lengths) + .map(|(d, l)| vec![d; l as usize]) + .flatten() + .collect::>(); + data + } + _ => { + let mut rest = vec![]; + obi_data + .read_to_end(&mut rest) + .map_err(|_| OBIError::Decode)?; + let data_points = rest + .iter() + .map(|&b| { + BitVec::::from_element(b) + .into_iter() + .map(|e| e as bool) + .collect::>() + }) + .flatten() + .collect::>(); + data_points + } + }; return Ok(Image { file_header, image_info_header, diff --git a/src/encode.rs b/src/encode.rs index a8c58e0..30edc49 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -3,8 +3,11 @@ use std::borrow::Borrow; use bitvec::{prelude::*, vec::BitVec}; use byteorder::{LittleEndian, WriteBytesExt}; -use crate::error::{OBIError, OBIResult}; -use crate::Image; +use crate::{ + error::{OBIError, OBIResult}, + rle::RLE, + CompressionType, Image, +}; pub fn encode_image(obi_image: I) -> OBIResult> where @@ -40,17 +43,38 @@ where .write_u32::(image_info_header.post_compression_size) .map_err(|_| OBIError::Encode)?; + let write_pixel_data = |pixels: &Vec, obi_data: &mut Vec| -> OBIResult<()> { + for byte in pixels.chunks(8) { + let mut bv = BitVec::::new(); + for &b in byte { + bv.push(b) + } + obi_data + .write_u8(bv.load::()) + .map_err(|_| OBIError::Encode)?; + } + Ok(()) + }; + // pixmap data let pixmap = &obi_image.data; - for byte in pixmap.chunks(8) { - let mut bv = BitVec::::new(); - for &b in byte { - bv.push(b); + match CompressionType::from_u32(obi_image.image_info_header.compression_type) { + CompressionType::RLE => { + let (data_points, lengths): (Vec<_>, Vec<_>) = pixmap.compress().into_iter().unzip(); + for l in lengths { + obi_data + .write_u32::(l as u32) + .map_err(|_| OBIError::Encode)?; + } + // end length sequence with zero + obi_data + .write_u32::(0) + .map_err(|_| OBIError::Encode)?; + // begin data point sequence + write_pixel_data(&data_points, &mut obi_data)?; } - obi_data - .write_u8(bv.load::()) - .map_err(|_| OBIError::Encode)?; - } + _ => write_pixel_data(pixmap, &mut obi_data)?, + }; return Ok(obi_data); } diff --git a/src/lib.rs b/src/lib.rs index f949d06..3995c16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,10 @@ use std::io; mod decode; mod encode; pub mod error; +mod rle; -use crate::error::{OBIError, OBIResult}; +use error::{OBIError, OBIResult}; +use rle::RLE; #[non_exhaustive] #[derive(Copy, Clone, Debug, PartialEq)] @@ -83,6 +85,14 @@ impl CompressionType { _ => panic!("Invalid compression algorithm"), } } + pub fn to_u32(self) -> u32 { + match self { + CompressionType::None => 0, + CompressionType::RLE => 1, + CompressionType::Kosinki => 10, + _ => panic!("Invalid compression algorithm"), + } + } } #[derive(Debug)] @@ -111,6 +121,10 @@ impl Image { } } + pub fn use_compression(&mut self, comp: CompressionType) { + self.image_info_header.compression_type = comp.to_u32(); + } + pub fn width(&self) -> u32 { self.image_info_header.width } diff --git a/src/rle.rs b/src/rle.rs new file mode 100644 index 0000000..f6d1e05 --- /dev/null +++ b/src/rle.rs @@ -0,0 +1,89 @@ +pub trait RLE { + type Data; + fn compress(&self) -> Vec>; + fn decompress(runs: Vec>) -> Vec; +} + +pub type Run = (T, usize); + +impl RLE for Vec +where + T: Clone + PartialEq, +{ + type Data = T; + fn compress(&self) -> Vec> { + let mut runs = vec![]; + if self.is_empty() { + return runs; + } + let mut idx = 0; + loop { + let first = &self[idx]; + let run_length = self[idx..].iter().take_while(|&item| item == first).count(); + + runs.push((first.clone(), run_length)); + + idx += run_length; + if idx > self.len() - 1 { + break; + } + } + runs + } + fn decompress(runs: Vec>) -> Vec { + runs.into_iter() + .map(|(item, size)| vec![item; size]) + .flatten() + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn singleton() { + let data = "a".chars().collect::>(); + assert_eq!(Vec::::compress(&data), vec![('a', 1),]); + } + #[test] + fn identity() { + let data = "aabbccddaabbaa".chars().collect::>(); + assert_eq!(Vec::::decompress(data.compress()), data); + } + #[test] + fn repeated_singleton() { + let data = "aaaaaaaaaaaaa".chars().collect::>(); + assert_eq!(Vec::::compress(&data), vec![('a', 13),]); + } + #[test] + fn empty_runs() { + let data = "".chars().collect::>(); + assert!(data.compress().is_empty()); + } + #[test] + fn empty_decompress() { + assert!(Vec::::decompress(vec![]).is_empty()); + } + #[test] + fn check_runs1() { + let data = "aaaabbbbcccc".chars().collect::>(); + assert_eq!(data.compress(), vec![('a', 4), ('b', 4), ('c', 4)]); + } + #[test] + fn check_runs2() { + let data = "aabbccddaabbaa".chars().collect::>(); + assert_eq!( + data.compress(), + vec![ + ('a', 2), + ('b', 2), + ('c', 2), + ('d', 2), + ('a', 2), + ('b', 2), + ('a', 2) + ] + ); + } +} diff --git a/tests/serde.rs b/tests/serde.rs index 295b8d0..3701707 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -11,13 +11,14 @@ fn size_of_image_info_header() { #[test] fn compression() { - let img = Image::new(50, 5); + let mut img = Image::new(50, 5); + img.use_compression(CompressionType::RLE); let encoded = img.encode().unwrap(); let mut cursor = Cursor::new(encoded); let decoded = Image::decode(&mut cursor).unwrap(); assert_eq!( CompressionType::from_u32(decoded.image_info_header.compression_type), - CompressionType::None + CompressionType::RLE ); } @@ -42,3 +43,20 @@ fn size_round_nearest() { assert_eq!(decoded.image_info_header.width, 50); assert_eq!(decoded.image_info_header.height, 5); } + +#[test] +fn size_compressed() { + let mut img = Image::new(100, 80); + img.use_compression(CompressionType::RLE); + let encoded = img.encode().unwrap(); + // 26 - headers + // 4 - first and only length + // 4 - separator zero + // 1 - first and only data point + assert_eq!(encoded.len(), 26 + 4 + 4 + 1); + let mut cursor = Cursor::new(encoded); + let decoded = Image::decode(&mut cursor).unwrap(); + assert_eq!(decoded.image_info_header.width, 100); + assert_eq!(decoded.image_info_header.height, 80); + assert!(decoded.data.iter().all(|x| !*x)); +} -- cgit v1.2.3