mirror of
https://github.com/emmanuel-marty/lzsa.git
synced 2025-02-18 00:30:44 +00:00
Check in LZSA2 implementation (ratio competitive with ZX7, faster decompression)
This commit is contained in:
parent
49b0739050
commit
8b7b4a2b4f
2
LICENSE
2
LICENSE
@ -1,3 +1,3 @@
|
||||
The LZSA code is available under the Zlib license, except for src/shrink.c which is placed under the Creative Commons CC0 license.
|
||||
The LZSA code is available under the Zlib license, except for src/matchfinder.c which is placed under the Creative Commons CC0 license.
|
||||
|
||||
Please consult LICENSE.zlib.md and LICENSE.CC0.md for more information.
|
||||
|
10
Makefile
10
Makefile
@ -10,10 +10,14 @@ $(OBJDIR)/%.o: src/../%.c
|
||||
|
||||
APP := lzsa
|
||||
|
||||
OBJS := $(OBJDIR)/src/main.o
|
||||
OBJS := $(OBJDIR)/src/lzsa.o
|
||||
OBJS += $(OBJDIR)/src/frame.o
|
||||
OBJS += $(OBJDIR)/src/shrink.o
|
||||
OBJS += $(OBJDIR)/src/expand.o
|
||||
OBJS += $(OBJDIR)/src/lib.o
|
||||
OBJS += $(OBJDIR)/src/matchfinder.o
|
||||
OBJS += $(OBJDIR)/src/shrink_v1.o
|
||||
OBJS += $(OBJDIR)/src/shrink_v2.o
|
||||
OBJS += $(OBJDIR)/src/expand_v1.o
|
||||
OBJS += $(OBJDIR)/src/expand_v2.o
|
||||
OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort.o
|
||||
OBJS += $(OBJDIR)/src/libdivsufsort/lib/sssort.o
|
||||
OBJS += $(OBJDIR)/src/libdivsufsort/lib/trsort.o
|
||||
|
@ -40,7 +40,7 @@ Inspirations:
|
||||
License:
|
||||
|
||||
* The LZSA code is available under the Zlib license.
|
||||
* The compressor (shrink.c) is available under the CC0 license due to using portions of code from Eric Bigger's Wimlib in the suffix array-based matchfinder.
|
||||
* The match finder (matchfinder.c) is available under the CC0 license due to using portions of code from Eric Bigger's Wimlib in the suffix array-based matchfinder.
|
||||
|
||||
# Stream format
|
||||
|
||||
|
@ -177,24 +177,32 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\expand.h" />
|
||||
<ClInclude Include="..\src\expand_v1.h" />
|
||||
<ClInclude Include="..\src\expand_v2.h" />
|
||||
<ClInclude Include="..\src\format.h" />
|
||||
<ClInclude Include="..\src\frame.h" />
|
||||
<ClInclude Include="..\src\lib.h" />
|
||||
<ClInclude Include="..\src\libdivsufsort\include\config.h" />
|
||||
<ClInclude Include="..\src\libdivsufsort\include\divsufsort.h" />
|
||||
<ClInclude Include="..\src\libdivsufsort\include\divsufsort_private.h" />
|
||||
<ClInclude Include="..\src\shrink.h" />
|
||||
<ClInclude Include="..\src\matchfinder.h" />
|
||||
<ClInclude Include="..\src\shrink_v1.h" />
|
||||
<ClInclude Include="..\src\shrink_v2.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\expand.c" />
|
||||
<ClCompile Include="..\src\expand_v1.c" />
|
||||
<ClCompile Include="..\src\expand_v2.c" />
|
||||
<ClCompile Include="..\src\frame.c" />
|
||||
<ClCompile Include="..\src\lib.c" />
|
||||
<ClCompile Include="..\src\libdivsufsort\lib\divsufsort.c" />
|
||||
<ClCompile Include="..\src\libdivsufsort\lib\sssort.c" />
|
||||
<ClCompile Include="..\src\libdivsufsort\lib\trsort.c" />
|
||||
<ClCompile Include="..\src\libdivsufsort\lib\utils.c" />
|
||||
<ClCompile Include="..\src\main.c" />
|
||||
<ClCompile Include="..\src\shrink.c" />
|
||||
<ClCompile Include="..\src\lzsa.c" />
|
||||
<ClCompile Include="..\src\matchfinder.c" />
|
||||
<ClCompile Include="..\src\shrink_v1.c" />
|
||||
<ClCompile Include="..\src\shrink_v2.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
@ -27,15 +27,9 @@
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Fichiers d%27en-tête</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\shrink.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\format.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\expand.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\libdivsufsort\include\config.h">
|
||||
<Filter>Fichiers sources\libdivsufsort\include</Filter>
|
||||
</ClInclude>
|
||||
@ -48,17 +42,26 @@
|
||||
<ClInclude Include="..\src\frame.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\expand_v2.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\shrink_v2.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\expand_v1.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\shrink_v1.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\matchfinder.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\lib.h">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\main.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\shrink.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\expand.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\libdivsufsort\lib\utils.c">
|
||||
<Filter>Fichiers sources\libdivsufsort\lib</Filter>
|
||||
</ClCompile>
|
||||
@ -74,5 +77,26 @@
|
||||
<ClCompile Include="..\src\frame.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\expand_v2.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\shrink_v2.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\expand_v1.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\shrink_v1.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\matchfinder.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\lib.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\lzsa.c">
|
||||
<Filter>Fichiers sources</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,5 +1,5 @@
|
||||
; -----------------------------------------------------------------------------
|
||||
; Decompress raw LZSA block. Create one with lzsa -r <original_file> <compressed_file>
|
||||
; Decompress raw LZSA1 block. Create one with lzsa -r <original_file> <compressed_file>
|
||||
;
|
||||
; in:
|
||||
; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address
|
||||
@ -31,7 +31,7 @@
|
||||
OFFSLO = $43 ; zero-page location for temp offset
|
||||
OFFSHI = $44
|
||||
|
||||
DECOMPRESS_LZSA
|
||||
DECOMPRESS_LZSA1
|
||||
LDY #$00
|
||||
|
||||
DECODE_TOKEN
|
245
asm/6502/decompress_v2.asm
Executable file
245
asm/6502/decompress_v2.asm
Executable file
@ -0,0 +1,245 @@
|
||||
; -----------------------------------------------------------------------------
|
||||
; Decompress raw LZSA2 block.
|
||||
; Create one with lzsa -r -f2 <original_file> <compressed_file>
|
||||
;
|
||||
; in:
|
||||
; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address
|
||||
; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address
|
||||
;
|
||||
; out:
|
||||
; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1
|
||||
; -----------------------------------------------------------------------------
|
||||
;
|
||||
; Copyright (C) 2019 Emmanuel Marty
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
OFFSLO = $43 ; zero-page location for temp offset
|
||||
OFFSHI = $44
|
||||
FIXUP = $4B
|
||||
NIBBLES = $FB
|
||||
NIBCOUNT = $FC
|
||||
|
||||
DECOMPRESS_LZSA2
|
||||
LDY #$00
|
||||
STY NIBBLES
|
||||
STY NIBCOUNT
|
||||
|
||||
DECODE_TOKEN
|
||||
JSR GETSRC ; read token byte: XYZ|LL|MMM
|
||||
PHA ; preserve token on stack
|
||||
|
||||
AND #$18 ; isolate literals count (LL)
|
||||
BEQ NO_LITERALS ; skip if no literals to copy
|
||||
CMP #$18 ; LITERALS_RUN_LEN_V2 << 3?
|
||||
BNE EMBEDDED_LITERALS ; if less, count is directly embedded in token
|
||||
|
||||
JSR GETNIBBLE ; get extra literals length nibble
|
||||
CLC ; add nibble to len from token
|
||||
ADC #$03 ; (LITERALS_RUN_LEN_V2)
|
||||
CMP #$12 ; LITERALS_RUN_LEN_V2 + 15 ?
|
||||
BNE PREPARE_COPY_LITERALS ; if less, literals count is complete
|
||||
|
||||
JSR GETSRC ; get extra byte of variable literals count
|
||||
TAX ; non-zero?
|
||||
BNE PREPARE_COPY_LITERALS_HIGH ; if so, literals count is complete
|
||||
|
||||
; handle 16 bits literals count
|
||||
; literals count = directly these 16 bits
|
||||
JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A
|
||||
TAY ; put high 8 bits in Y
|
||||
JMP PREPARE_COPY_LITERALS_HIGH
|
||||
|
||||
EMBEDDED_LITERALS
|
||||
LSR A ; shift literals count into place
|
||||
LSR A
|
||||
LSR A
|
||||
|
||||
PREPARE_COPY_LITERALS
|
||||
TAX
|
||||
PREPARE_COPY_LITERALS_HIGH
|
||||
INY
|
||||
|
||||
COPY_LITERALS
|
||||
JSR GETPUT ; copy one byte of literals
|
||||
DEX
|
||||
BNE COPY_LITERALS
|
||||
DEY
|
||||
BNE COPY_LITERALS
|
||||
|
||||
NO_LITERALS
|
||||
PLA ; retrieve token from stack
|
||||
PHA ; preserve token again
|
||||
BMI REPMATCH_OR_LARGE_OFFSET ; 1YZ: rep-match or 13/16 bit offset
|
||||
|
||||
ASL ; 0YZ: 5 or 9 bit offset
|
||||
BMI OFFSET_9_BIT
|
||||
|
||||
; 00Z: 5 bit offset
|
||||
LSR A ; Shift Z (offset bit 4) in place
|
||||
LSR A
|
||||
AND #$10
|
||||
STA FIXUP
|
||||
|
||||
JSR GETNIBBLE ; get nibble for offset bits 0-3
|
||||
ORA FIXUP ; merge offset bit 4
|
||||
ORA #$E0 ; set offset bits 7-5 to 1
|
||||
TAX ; store low byte of match offset
|
||||
LDA #$0FF ; set offset bits 15-8 to 1
|
||||
BNE GOT_OFFSET ; (*same as JMP GOT_OFFSET but shorter)
|
||||
|
||||
OFFSET_9_BIT ; 01Z: 9 bit offset
|
||||
ASL ; shift Z (offset bit 8) in place
|
||||
ROL
|
||||
ROL
|
||||
ORA #$FE ; set offset bits 15-9 to 1
|
||||
STA OFFSHI
|
||||
|
||||
JSR GETSRC ; get offset bits 0-7 from stream in A
|
||||
TAX ; store low byte of match offset
|
||||
JMP GOT_OFFSET_LO ; go prepare match
|
||||
|
||||
REPMATCH_OR_LARGE_OFFSET
|
||||
ASL ; 13 bit offset?
|
||||
BMI REPMATCH_OR_16_BIT ; handle rep-match or 16-bit offset if not
|
||||
|
||||
; 10Z: 13 bit offset
|
||||
|
||||
LSR A ; shift Z (offset bit 4) in place
|
||||
LSR A
|
||||
AND #$10
|
||||
STA FIXUP
|
||||
|
||||
JSR GETSRC ; get offset bits 0-7 from stream in A
|
||||
TAX ; store low byte of match offset
|
||||
|
||||
JSR GETNIBBLE ; get nibble for offset bits 8-11
|
||||
ORA FIXUP ; merge offset bit 12
|
||||
CLC
|
||||
ADC #$DE ; set bits 13-15 to 1 and substract 2 (to substract 512)
|
||||
BNE GOT_OFFSET ; go prepare match (*same as JMP GOT_OFFSET but shorter)
|
||||
|
||||
REPMATCH_OR_16_BIT ; rep-match or 16 bit offset
|
||||
ASL ; XYZ=111?
|
||||
BMI REP_MATCH ; reuse previous offset if so (rep-match)
|
||||
|
||||
; 110: handle 16 bit offset
|
||||
JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A
|
||||
|
||||
GOT_OFFSET
|
||||
STA OFFSHI ; store final match offset
|
||||
GOT_OFFSET_LO
|
||||
STX OFFSLO
|
||||
|
||||
REP_MATCH
|
||||
CLC ; add dest + match offset
|
||||
LDA PUTDST+1 ; low 8 bits
|
||||
ADC OFFSLO
|
||||
STA COPY_MATCH_LOOP+1 ; store back reference address
|
||||
LDA OFFSHI ; high 8 bits
|
||||
ADC PUTDST+2
|
||||
STA COPY_MATCH_LOOP+2 ; store high 8 bits of address
|
||||
|
||||
PLA ; retrieve token from stack again
|
||||
AND #$07 ; isolate match len (MMM)
|
||||
CLC
|
||||
ADC #$02 ; add MIN_MATCH_SIZE_V2
|
||||
CMP #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2?
|
||||
BNE PREPARE_COPY_MATCH ; if less, length is directly embedded in token
|
||||
|
||||
JSR GETNIBBLE ; get extra match length nibble
|
||||
CLC ; add nibble to len from token
|
||||
ADC #$09 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2)
|
||||
CMP #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15?
|
||||
BNE PREPARE_COPY_MATCH ; if less, match length is complete
|
||||
|
||||
JSR GETSRC ; get extra byte of variable match length
|
||||
TAX ; non-zero?
|
||||
BNE PREPARE_COPY_MATCH_Y ; if so, the match length is complete
|
||||
|
||||
; Handle 16 bits match length
|
||||
JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A
|
||||
TAY ; put high 8 bits in Y
|
||||
; large match length with zero high byte?
|
||||
BEQ DECOMPRESSION_DONE ; if so, this is the EOD code, bail
|
||||
TXA
|
||||
|
||||
PREPARE_COPY_MATCH
|
||||
TAX
|
||||
PREPARE_COPY_MATCH_Y
|
||||
INY
|
||||
|
||||
COPY_MATCH_LOOP
|
||||
LDA $AAAA ; get one byte of backreference
|
||||
INC COPY_MATCH_LOOP+1
|
||||
BNE GETMATCH_DONE
|
||||
INC COPY_MATCH_LOOP+2
|
||||
GETMATCH_DONE
|
||||
JSR PUTDST ; copy to destination
|
||||
DEX
|
||||
BNE COPY_MATCH_LOOP
|
||||
DEY
|
||||
BNE COPY_MATCH_LOOP
|
||||
JMP DECODE_TOKEN
|
||||
|
||||
GETNIBBLE
|
||||
DEC NIBCOUNT
|
||||
BPL HAS_NIBBLES
|
||||
|
||||
LDA #$01
|
||||
STA NIBCOUNT
|
||||
JSR GETSRC ; get 2 nibbles
|
||||
STA NIBBLES
|
||||
LSR A
|
||||
LSR A
|
||||
LSR A
|
||||
LSR A
|
||||
RTS
|
||||
|
||||
HAS_NIBBLES
|
||||
LDA NIBBLES
|
||||
AND #$0F ; isolate low 4 bits of nibble
|
||||
RTS
|
||||
|
||||
GETPUT
|
||||
JSR GETSRC
|
||||
PUTDST
|
||||
LZSA_DST_LO = *+1
|
||||
LZSA_DST_HI = *+2
|
||||
STA $AAAA
|
||||
INC PUTDST+1
|
||||
BNE PUTDST_DONE
|
||||
INC PUTDST+2
|
||||
PUTDST_DONE
|
||||
DECOMPRESSION_DONE
|
||||
RTS
|
||||
|
||||
GETLARGESRC
|
||||
JSR GETSRC ; grab low 8 bits
|
||||
TAX ; move to X
|
||||
; fall through grab high 8 bits
|
||||
|
||||
GETSRC
|
||||
LZSA_SRC_LO = *+1
|
||||
LZSA_SRC_HI = *+2
|
||||
LDA $AAAA
|
||||
INC GETSRC+1
|
||||
BNE GETSRC_DONE
|
||||
INC GETSRC+2
|
||||
GETSRC_DONE
|
||||
RTS
|
@ -22,15 +22,15 @@
|
||||
bits 16
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Decompress raw LZSA block
|
||||
; Decompress raw LZSA1 block
|
||||
; inputs:
|
||||
; * ds:si: raw LZSA block
|
||||
; * ds:si: raw LZSA1 block
|
||||
; * es:di: output buffer
|
||||
; output:
|
||||
; * ax: decompressed size
|
||||
; ---------------------------------------------------------------------------
|
||||
|
||||
lzsa_decompress:
|
||||
lzsa1_decompress:
|
||||
push di ; remember decompression offset
|
||||
cld ; make string operations (lods, movs, stos..) move forward
|
||||
|
174
asm/8088/decompress_small_v2.S
Executable file
174
asm/8088/decompress_small_v2.S
Executable file
@ -0,0 +1,174 @@
|
||||
; decompress_small.S - space-efficient decompressor implementation for 8088
|
||||
;
|
||||
; Copyright (C) 2019 Emmanuel Marty
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
segment .text
|
||||
bits 16
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Decompress raw LZSA2 block
|
||||
; inputs:
|
||||
; * ds:si: raw LZSA2 block
|
||||
; * es:di: output buffer
|
||||
; output:
|
||||
; * ax: decompressed size
|
||||
; ---------------------------------------------------------------------------
|
||||
|
||||
lzsa2_decompress:
|
||||
push di ; remember decompression offset
|
||||
cld ; make string operations (lods, movs, stos..) move forward
|
||||
|
||||
xor cx,cx
|
||||
xor bx,bx
|
||||
xor bp,bp
|
||||
|
||||
.decode_token:
|
||||
mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match
|
||||
lodsb ; read token byte: XYZ|LL|MMMM
|
||||
mov dx,ax ; keep token in dl
|
||||
|
||||
and al,018H ; isolate literals length in token (LL)
|
||||
mov cl,3
|
||||
shr al,cl ; shift literals length into place
|
||||
|
||||
cmp al,03H ; LITERALS_RUN_LEN_V2?
|
||||
jne .got_literals ; no, we have the full literals count from the token, go copy
|
||||
|
||||
call .get_nibble ; get extra literals length nibble
|
||||
add al,cl ; add len from token to nibble
|
||||
cmp al,012H ; LITERALS_RUN_LEN_V2 + 15 ?
|
||||
jne .got_literals ; if not, we have the full literals count, go copy
|
||||
|
||||
lodsb ; grab extra length byte
|
||||
test al,al ; zero?
|
||||
jne .got_literals ; if not, we have the full literals count, go copy
|
||||
|
||||
lodsw ; grab 16-bit extra length
|
||||
|
||||
.got_literals:
|
||||
xchg cx,ax
|
||||
rep movsb ; copy cx literals from ds:si to es:di
|
||||
|
||||
test dl,dl ; check match offset mode in token (X bit)
|
||||
js .rep_match_or_large_offset
|
||||
|
||||
cmp dl,040H ; check if this is a 5 or 9-bit offset (Y bit)
|
||||
jnb .offset_9_bit
|
||||
|
||||
; 5 bit offset
|
||||
xchg ax,cx ; clear ah - cx is zero from the rep movsb above
|
||||
mov al,020H ; shift Z (offset bit 4) in place
|
||||
and al,dl
|
||||
shr al,1
|
||||
call .get_nibble ; get nibble for offset bits 0-3
|
||||
or al,cl ; merge nibble
|
||||
or al,0E0H ; set offset bits 7-5 to 1
|
||||
dec ah ; set offset bits 15-8 to 1
|
||||
jmp short .get_match_length
|
||||
|
||||
.offset_9_bit: ; 9 bit offset
|
||||
xchg ax,cx ; clear ah - cx is zero from the rep movsb above
|
||||
lodsb ; get 8 bit offset from stream in A
|
||||
dec ah ; set offset bits 15-8 to 1
|
||||
test dl,020H ; test bit Z (offset bit 8)
|
||||
jne .get_match_length
|
||||
dec ah ; clear bit 8 if Z bit is clear
|
||||
jmp short .get_match_length
|
||||
|
||||
.rep_match_or_large_offset:
|
||||
cmp dl,0c0H ; check if this is a 13-bit offset or a 16-bit offset/rep match (Y bit)
|
||||
jnb .rep_match_or_16_bit
|
||||
|
||||
; 13 bit offset
|
||||
lodsb ; load match offset bits 0-7
|
||||
|
||||
mov ah,020H ; shift Z (offset bit 12) in place
|
||||
and ah,dl
|
||||
shr ah,1
|
||||
call .get_nibble ; get nibble for offset bits 8-11
|
||||
or ah,cl ; merge nibble
|
||||
or ah,0E0H ; set offset bits 15-13 to 1
|
||||
sub ah,2 ; substract 512
|
||||
jmp short .get_match_length
|
||||
|
||||
.rep_match_or_16_bit:
|
||||
test dl,020H ; test bit Z (offset bit 8)
|
||||
jne .repeat_match ; rep-match
|
||||
|
||||
; 16 bit offset
|
||||
lodsw ; Get 2-byte match offset
|
||||
|
||||
.get_match_length:
|
||||
mov bp,ax ; bp: offset
|
||||
.repeat_match:
|
||||
mov ax,dx ; ax: original token
|
||||
and al,07H ; isolate match length in token (MMM)
|
||||
add al,2 ; add MIN_MATCH_SIZE_V2
|
||||
|
||||
cmp al,09H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2?
|
||||
jne .got_matchlen ; no, we have the full match length from the token, go copy
|
||||
|
||||
call .get_nibble ; get extra literals length nibble
|
||||
add al,cl ; add len from token to nibble
|
||||
cmp al,018H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15?
|
||||
jne .got_matchlen ; no, we have the full match length from the token, go copy
|
||||
|
||||
lodsb ; grab extra length byte
|
||||
test al,al ; zero?
|
||||
jne .got_matchlen ; if not, we have the entire length
|
||||
|
||||
lodsw ; grab 16-bit length
|
||||
test ax,ax ; bail if we hit EOD
|
||||
je short .done_decompressing
|
||||
|
||||
.got_matchlen:
|
||||
xchg cx,ax ; copy match length into cx
|
||||
push ds ; save ds:si (current pointer to compressed data)
|
||||
xchg si,ax
|
||||
push es
|
||||
pop ds
|
||||
mov si,di ; ds:si now points at back reference in output data
|
||||
add si,bp
|
||||
rep movsb ; copy match
|
||||
xchg si,ax ; restore ds:si
|
||||
pop ds
|
||||
jmp .decode_token ; go decode another token
|
||||
|
||||
.done_decompressing:
|
||||
pop ax ; retrieve the original decompression offset
|
||||
xchg ax,di ; compute decompressed size
|
||||
sub ax,di
|
||||
ret ; done
|
||||
|
||||
.get_nibble:
|
||||
dec bh ; nibble ready?
|
||||
jns .has_nibble
|
||||
|
||||
mov cx,ax
|
||||
lodsb ; load two nibbles
|
||||
mov bl,al
|
||||
mov bh,1
|
||||
mov ax,cx
|
||||
|
||||
.has_nibble:
|
||||
mov cl,4 ; swap 4 high and low bits of nibble
|
||||
ror bl,cl
|
||||
mov cl,0FH
|
||||
and cl,bl
|
||||
ret
|
56
src/expand.c → src/expand_v1.c
Executable file → Normal file
56
src/expand.c → src/expand_v1.c
Executable file → Normal file
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* expand.c - block decompressor implementation
|
||||
* expand_v1.c - LZSA1 block decompressor implementation
|
||||
*
|
||||
* Copyright (C) 2019 Emmanuel Marty
|
||||
*
|
||||
@ -20,11 +20,21 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "format.h"
|
||||
#include "expand.h"
|
||||
#include "expand_v1.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define FORCE_INLINE __forceinline
|
||||
@ -32,11 +42,11 @@
|
||||
#define FORCE_INLINE __attribute__((always_inline))
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
static inline FORCE_INLINE int lzsa_expand_literals_slow(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int nLiterals, unsigned char **ppCurOutData, const unsigned char *pOutDataEnd) {
|
||||
static inline FORCE_INLINE int lzsa_expand_literals_slow_v1(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int nLiterals, unsigned char **ppCurOutData, const unsigned char *pOutDataEnd) {
|
||||
const unsigned char *pInBlock = *ppInBlock;
|
||||
unsigned char *pCurOutData = *ppCurOutData;
|
||||
|
||||
if (nLiterals == LITERALS_RUN_LEN) {
|
||||
if (nLiterals == LITERALS_RUN_LEN_V1) {
|
||||
unsigned char nByte;
|
||||
|
||||
if (pInBlock < pInBlockEnd) {
|
||||
@ -83,12 +93,12 @@ static inline FORCE_INLINE int lzsa_expand_literals_slow(const unsigned char **p
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline FORCE_INLINE int lzsa_expand_match_slow(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, const unsigned char *pSrc, unsigned int nMatchLen, unsigned char **ppCurOutData, const unsigned char *pOutDataEnd, const unsigned char *pOutDataFastEnd) {
|
||||
static inline FORCE_INLINE int lzsa_expand_match_slow_v1(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, const unsigned char *pSrc, unsigned int nMatchLen, unsigned char **ppCurOutData, const unsigned char *pOutDataEnd, const unsigned char *pOutDataFastEnd) {
|
||||
const unsigned char *pInBlock = *ppInBlock;
|
||||
unsigned char *pCurOutData = *ppCurOutData;
|
||||
|
||||
nMatchLen += MIN_MATCH_SIZE;
|
||||
if (nMatchLen == (MATCH_RUN_LEN + MIN_MATCH_SIZE)) {
|
||||
nMatchLen += MIN_MATCH_SIZE_V1;
|
||||
if (nMatchLen == (MATCH_RUN_LEN_V1 + MIN_MATCH_SIZE_V1)) {
|
||||
unsigned char nByte;
|
||||
|
||||
if (pInBlock < pInBlockEnd) {
|
||||
@ -159,7 +169,7 @@ static inline FORCE_INLINE int lzsa_expand_match_slow(const unsigned char **ppIn
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress one data block
|
||||
* Decompress one LZSA1 data block
|
||||
*
|
||||
* @param pInBlock pointer to compressed data
|
||||
* @param nInBlockSize size of compressed data, in bytes
|
||||
@ -169,7 +179,7 @@ static inline FORCE_INLINE int lzsa_expand_match_slow(const unsigned char **ppIn
|
||||
*
|
||||
* @return size of decompressed data in bytes, or -1 for error
|
||||
*/
|
||||
int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) {
|
||||
int lzsa_expand_block_v1(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) {
|
||||
const unsigned char *pInBlockEnd = pInBlock + nBlockSize;
|
||||
const unsigned char *pInBlockFastEnd = pInBlock + nBlockSize - 8;
|
||||
unsigned char *pCurOutData = pOutData + nOutDataOffset;
|
||||
@ -182,36 +192,35 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
const unsigned char token = *pInBlock++;
|
||||
unsigned int nLiterals = (unsigned int)((token & 0x70) >> 4);
|
||||
|
||||
if (nLiterals < LITERALS_RUN_LEN) {
|
||||
if (nLiterals < LITERALS_RUN_LEN_V1) {
|
||||
memcpy(pCurOutData, pInBlock, 8);
|
||||
pInBlock += nLiterals;
|
||||
pCurOutData += nLiterals;
|
||||
}
|
||||
else {
|
||||
if (lzsa_expand_literals_slow(&pInBlock, pInBlockEnd, nLiterals, &pCurOutData, pOutDataEnd))
|
||||
if (lzsa_expand_literals_slow_v1(&pInBlock, pInBlockEnd, nLiterals, &pCurOutData, pOutDataEnd))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((pInBlock + 1) < pInBlockEnd) { /* The last token in the block does not include match information */
|
||||
int nMatchOffset;
|
||||
|
||||
nMatchOffset = ((unsigned int)(*pInBlock++ ^ 0xff));
|
||||
nMatchOffset = ((unsigned int)(*pInBlock++)) | 0xffffff00;
|
||||
if (token & 0x80) {
|
||||
nMatchOffset |= (((unsigned int)(*pInBlock++ ^ 0xff)) << 8);
|
||||
nMatchOffset = (nMatchOffset & 0xffff00ff) | (((unsigned int)(*pInBlock++)) << 8);
|
||||
}
|
||||
nMatchOffset++;
|
||||
|
||||
const unsigned char *pSrc = pCurOutData - nMatchOffset;
|
||||
const unsigned char *pSrc = pCurOutData + nMatchOffset;
|
||||
if (pSrc >= pOutData) {
|
||||
unsigned int nMatchLen = (unsigned int)(token & 0x0f);
|
||||
if (nMatchLen < MATCH_RUN_LEN && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd) {
|
||||
if (nMatchLen < MATCH_RUN_LEN_V1 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd) {
|
||||
memcpy(pCurOutData, pSrc, 8);
|
||||
memcpy(pCurOutData + 8, pSrc + 8, 8);
|
||||
memcpy(pCurOutData + 16, pSrc + 16, 4);
|
||||
pCurOutData += (MIN_MATCH_SIZE + nMatchLen);
|
||||
pCurOutData += (MIN_MATCH_SIZE_V1 + nMatchLen);
|
||||
}
|
||||
else {
|
||||
if (lzsa_expand_match_slow(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
if (lzsa_expand_match_slow_v1(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -227,22 +236,21 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
const unsigned char token = *pInBlock++;
|
||||
unsigned int nLiterals = (unsigned int)((token & 0x70) >> 4);
|
||||
|
||||
if (lzsa_expand_literals_slow(&pInBlock, pInBlockEnd, nLiterals, &pCurOutData, pOutDataEnd))
|
||||
if (lzsa_expand_literals_slow_v1(&pInBlock, pInBlockEnd, nLiterals, &pCurOutData, pOutDataEnd))
|
||||
return -1;
|
||||
|
||||
if ((pInBlock + 1) < pInBlockEnd) { /* The last token in the block does not include match information */
|
||||
int nMatchOffset;
|
||||
|
||||
nMatchOffset = ((unsigned int)(*pInBlock++ ^ 0xff));
|
||||
nMatchOffset = ((unsigned int)(*pInBlock++)) | 0xffffff00;
|
||||
if (token & 0x80) {
|
||||
nMatchOffset |= (((unsigned int)(*pInBlock++ ^ 0xff)) << 8);
|
||||
nMatchOffset = (nMatchOffset & 0xffff00ff) | (((unsigned int)(*pInBlock++)) << 8);
|
||||
}
|
||||
nMatchOffset++;
|
||||
|
||||
const unsigned char *pSrc = pCurOutData - nMatchOffset;
|
||||
const unsigned char *pSrc = pCurOutData + nMatchOffset;
|
||||
if (pSrc >= pOutData) {
|
||||
unsigned int nMatchLen = (unsigned int)(token & 0x0f);
|
||||
if (lzsa_expand_match_slow(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
if (lzsa_expand_match_slow_v1(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
return -1;
|
||||
}
|
||||
else {
|
22
src/expand.h → src/expand_v1.h
Executable file → Normal file
22
src/expand.h → src/expand_v1.h
Executable file → Normal file
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* expand.h - block decompressor definitions
|
||||
* expand_v1.h - LZSA1 block decompressor definitions
|
||||
*
|
||||
* Copyright (C) 2019 Emmanuel Marty
|
||||
*
|
||||
@ -20,11 +20,21 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _EXPAND_H
|
||||
#define _EXPAND_H
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _EXPAND_V1_H
|
||||
#define _EXPAND_V1_H
|
||||
|
||||
/**
|
||||
* Decompress one data block
|
||||
* Decompress one LZSA1 data block
|
||||
*
|
||||
* @param pInBlock pointer to compressed data
|
||||
* @param nInBlockSize size of compressed data, in bytes
|
||||
@ -34,6 +44,6 @@
|
||||
*
|
||||
* @return size of decompressed data in bytes, or -1 for error
|
||||
*/
|
||||
int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize);
|
||||
int lzsa_expand_block_v1(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize);
|
||||
|
||||
#endif /* _EXPAND_H */
|
||||
#endif /* _EXPAND_V1_H */
|
330
src/expand_v2.c
Normal file
330
src/expand_v2.c
Normal file
@ -0,0 +1,330 @@
|
||||
/*
|
||||
* expand_v2.c - LZSA2 block decompressor implementation
|
||||
*
|
||||
* Copyright (C) 2019 Emmanuel Marty
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "format.h"
|
||||
#include "expand_v2.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define FORCE_INLINE __forceinline
|
||||
#else /* _MSC_VER */
|
||||
#define FORCE_INLINE __attribute__((always_inline))
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
static inline FORCE_INLINE unsigned int lzsa_get_nibble_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, int *nCurNibbles, unsigned char *nibbles) {
|
||||
unsigned int nValue;
|
||||
|
||||
if ((*nCurNibbles ^= 1) != 0) {
|
||||
const unsigned char *pInBlock = *ppInBlock;
|
||||
if (pInBlock >= pInBlockEnd) return -1;
|
||||
(*nibbles) = *pInBlock++;
|
||||
*ppInBlock = pInBlock;
|
||||
}
|
||||
|
||||
nValue = ((unsigned int)((*nibbles) & 0xf0)) >> 4;
|
||||
|
||||
(*nibbles) <<= 4;
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
static inline FORCE_INLINE int lzsa_expand_literals_slow_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int nLiterals, int *nCurNibbles, unsigned char *nibbles,
|
||||
unsigned char **ppCurOutData, const unsigned char *pOutDataEnd) {
|
||||
const unsigned char *pInBlock = *ppInBlock;
|
||||
unsigned char *pCurOutData = *ppCurOutData;
|
||||
|
||||
if (nLiterals == LITERALS_RUN_LEN_V2) {
|
||||
nLiterals += lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, nCurNibbles, nibbles);
|
||||
|
||||
if (nLiterals == (LITERALS_RUN_LEN_V2 + 15)) {
|
||||
if (pInBlock < pInBlockEnd) {
|
||||
nLiterals = ((unsigned int)*pInBlock++);
|
||||
|
||||
if (nLiterals == 0) {
|
||||
if ((pInBlock + 1) < pInBlockEnd) {
|
||||
nLiterals = ((unsigned int)*pInBlock++);
|
||||
nLiterals |= (((unsigned int)*pInBlock++) << 8);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nLiterals != 0) {
|
||||
if ((pInBlock + nLiterals) <= pInBlockEnd &&
|
||||
(pCurOutData + nLiterals) <= pOutDataEnd) {
|
||||
memcpy(pCurOutData, pInBlock, nLiterals);
|
||||
pInBlock += nLiterals;
|
||||
pCurOutData += nLiterals;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*ppInBlock = pInBlock;
|
||||
*ppCurOutData = pCurOutData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline FORCE_INLINE int lzsa_expand_match_slow_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, const unsigned char *pSrc, unsigned int nMatchLen, int *nCurNibbles, unsigned char *nibbles,
|
||||
unsigned char **ppCurOutData, const unsigned char *pOutDataEnd, const unsigned char *pOutDataFastEnd) {
|
||||
const unsigned char *pInBlock = *ppInBlock;
|
||||
unsigned char *pCurOutData = *ppCurOutData;
|
||||
|
||||
nMatchLen += MIN_MATCH_SIZE_V2;
|
||||
if (nMatchLen == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2)) {
|
||||
nMatchLen += lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, nCurNibbles, nibbles);
|
||||
|
||||
if (nMatchLen == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2 + 15)) {
|
||||
if (pInBlock < pInBlockEnd) {
|
||||
nMatchLen = ((unsigned int)*pInBlock++);
|
||||
|
||||
if (nMatchLen == 0) {
|
||||
if ((pInBlock + 1) < pInBlockEnd) {
|
||||
nMatchLen = ((unsigned int)*pInBlock++);
|
||||
nMatchLen |= (((unsigned int)*pInBlock++) << 8);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((pCurOutData + nMatchLen) <= pOutDataEnd) {
|
||||
/* Do a deterministic, left to right byte copy instead of memcpy() so as to handle overlaps */
|
||||
|
||||
if ((pCurOutData - pSrc) >= 8 && (pCurOutData + nMatchLen) < (pOutDataFastEnd - 15)) {
|
||||
const unsigned char *pCopySrc = pSrc;
|
||||
unsigned char *pCopyDst = pCurOutData;
|
||||
const unsigned char *pCopyEndDst = pCurOutData + nMatchLen;
|
||||
|
||||
do {
|
||||
memcpy(pCopyDst, pCopySrc, 8);
|
||||
memcpy(pCopyDst + 8, pCopySrc + 8, 8);
|
||||
pCopySrc += 16;
|
||||
pCopyDst += 16;
|
||||
} while (pCopyDst < pCopyEndDst);
|
||||
|
||||
pCurOutData += nMatchLen;
|
||||
}
|
||||
else {
|
||||
while (nMatchLen >= 4) {
|
||||
*pCurOutData++ = *pSrc++;
|
||||
*pCurOutData++ = *pSrc++;
|
||||
*pCurOutData++ = *pSrc++;
|
||||
*pCurOutData++ = *pSrc++;
|
||||
nMatchLen -= 4;
|
||||
}
|
||||
while (nMatchLen) {
|
||||
*pCurOutData++ = *pSrc++;
|
||||
nMatchLen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ppInBlock = pInBlock;
|
||||
*ppCurOutData = pCurOutData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress one LZSA2 data block
|
||||
*
|
||||
* @param pInBlock pointer to compressed data
|
||||
* @param nInBlockSize size of compressed data, in bytes
|
||||
* @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block)
|
||||
* @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes)
|
||||
* @param nBlockMaxSize total size of output decompression buffer, in bytes
|
||||
*
|
||||
* @return size of decompressed data in bytes, or -1 for error
|
||||
*/
|
||||
int lzsa_expand_block_v2(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) {
|
||||
const unsigned char *pInBlockEnd = pInBlock + nBlockSize;
|
||||
const unsigned char *pInBlockFastEnd = pInBlock + nBlockSize - 8;
|
||||
unsigned char *pCurOutData = pOutData + nOutDataOffset;
|
||||
const unsigned char *pOutDataEnd = pCurOutData + nBlockMaxSize;
|
||||
const unsigned char *pOutDataFastEnd = pOutDataEnd - 20;
|
||||
int nCurNibbles = 0;
|
||||
unsigned char nibbles;
|
||||
int nMatchOffset = 0;
|
||||
|
||||
/* Fast loop */
|
||||
|
||||
while (pInBlock < pInBlockFastEnd && pCurOutData < pOutDataFastEnd) {
|
||||
const unsigned char token = *pInBlock++;
|
||||
unsigned int nLiterals = (unsigned int)((token & 0x18) >> 3);
|
||||
|
||||
if (nLiterals < LITERALS_RUN_LEN_V2) {
|
||||
memcpy(pCurOutData, pInBlock, 8);
|
||||
pInBlock += nLiterals;
|
||||
pCurOutData += nLiterals;
|
||||
}
|
||||
else {
|
||||
if (lzsa_expand_literals_slow_v2(&pInBlock, pInBlockEnd, nLiterals, &nCurNibbles, &nibbles, &pCurOutData, pOutDataEnd))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((pInBlock + 1) < pInBlockEnd) { /* The last token in the block does not include match information */
|
||||
unsigned char nOffsetMode = token & 0xc0;
|
||||
|
||||
switch (nOffsetMode) {
|
||||
case 0x00:
|
||||
/* 5 bit offset */
|
||||
nMatchOffset = (unsigned int)lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles);
|
||||
nMatchOffset |= ((token & 0x20) >> 1);
|
||||
nMatchOffset |= 0xffffffe0;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
/* 9 bit offset */
|
||||
nMatchOffset = (unsigned int)(*pInBlock++);
|
||||
nMatchOffset |= (((unsigned int)(token & 0x20)) << 3);
|
||||
nMatchOffset |= 0xfffffe00;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
/* 13 bit offset */
|
||||
nMatchOffset = (unsigned int)(*pInBlock++);
|
||||
nMatchOffset |= (lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles) << 8);
|
||||
nMatchOffset |= (((unsigned int)(token & 0x20)) << 7);
|
||||
nMatchOffset |= 0xffffe000;
|
||||
nMatchOffset -= 512;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Check if this is a 16 bit offset or a rep-match */
|
||||
if ((token & 0x20) == 0) {
|
||||
/* 16 bit offset */
|
||||
nMatchOffset = (unsigned int)(*pInBlock++);
|
||||
nMatchOffset |= (((unsigned int)(*pInBlock++)) << 8);
|
||||
nMatchOffset |= 0xffff0000;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const unsigned char *pSrc = pCurOutData + nMatchOffset;
|
||||
if (pSrc >= pOutData) {
|
||||
unsigned int nMatchLen = (unsigned int)(token & 0x07);
|
||||
if (nMatchLen < MATCH_RUN_LEN_V2 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd) {
|
||||
memcpy(pCurOutData, pSrc, 8);
|
||||
memcpy(pCurOutData + 8, pSrc + 8, 4);
|
||||
pCurOutData += (MIN_MATCH_SIZE_V2 + nMatchLen);
|
||||
}
|
||||
else {
|
||||
if (lzsa_expand_match_slow_v2(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &nCurNibbles, &nibbles, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow loop for the remainder of the buffer */
|
||||
|
||||
while (pInBlock < pInBlockEnd) {
|
||||
const unsigned char token = *pInBlock++;
|
||||
unsigned int nLiterals = (unsigned int)((token & 0x18) >> 3);
|
||||
|
||||
if (lzsa_expand_literals_slow_v2(&pInBlock, pInBlockEnd, nLiterals, &nCurNibbles, &nibbles, &pCurOutData, pOutDataEnd))
|
||||
return -1;
|
||||
|
||||
if ((pInBlock + 1) < pInBlockEnd) { /* The last token in the block does not include match information */
|
||||
unsigned char nOffsetMode = token & 0xc0;
|
||||
|
||||
switch (nOffsetMode) {
|
||||
case 0x00:
|
||||
/* 5 bit offset */
|
||||
nMatchOffset = (unsigned int)lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles);
|
||||
nMatchOffset |= ((token & 0x20) >> 1);
|
||||
nMatchOffset |= 0xffffffe0;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
/* 9 bit offset */
|
||||
nMatchOffset = (unsigned int)(*pInBlock++);
|
||||
nMatchOffset |= (((unsigned int)(token & 0x20)) << 3);
|
||||
nMatchOffset |= 0xfffffe00;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
/* 13 bit offset */
|
||||
nMatchOffset = (unsigned int)(*pInBlock++);
|
||||
nMatchOffset |= (lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles) << 8);
|
||||
nMatchOffset |= (((unsigned int)(token & 0x20)) << 7);
|
||||
nMatchOffset |= 0xffffe000;
|
||||
nMatchOffset -= 512;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Check if this is a 16 bit offset or a rep-match */
|
||||
if ((token & 0x20) == 0) {
|
||||
/* 16 bit offset */
|
||||
nMatchOffset = (unsigned int)(*pInBlock++);
|
||||
nMatchOffset |= (((unsigned int)(*pInBlock++)) << 8);
|
||||
nMatchOffset |= 0xffff0000;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const unsigned char *pSrc = pCurOutData + nMatchOffset;
|
||||
if (pSrc >= pOutData) {
|
||||
unsigned int nMatchLen = (unsigned int)(token & 0x07);
|
||||
if (lzsa_expand_match_slow_v2(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &nCurNibbles, &nibbles, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(pCurOutData - (pOutData + nOutDataOffset));
|
||||
}
|
49
src/expand_v2.h
Normal file
49
src/expand_v2.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* expand_v2.h - LZSA2 block decompressor definitions
|
||||
*
|
||||
* Copyright (C) 2019 Emmanuel Marty
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _EXPAND_V2_H
|
||||
#define _EXPAND_V2_H
|
||||
|
||||
/**
|
||||
* Decompress one LZSA2 data block
|
||||
*
|
||||
* @param pInBlock pointer to compressed data
|
||||
* @param nInBlockSize size of compressed data, in bytes
|
||||
* @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block)
|
||||
* @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes)
|
||||
* @param nBlockMaxSize total size of output decompression buffer, in bytes
|
||||
*
|
||||
* @return size of decompressed data in bytes, or -1 for error
|
||||
*/
|
||||
int lzsa_expand_block_v2(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize);
|
||||
|
||||
#endif /* _EXPAND_V2_H */
|
21
src/format.h
21
src/format.h
@ -20,13 +20,28 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FORMAT_H
|
||||
#define _FORMAT_H
|
||||
|
||||
#define MIN_MATCH_SIZE 3
|
||||
#define MIN_OFFSET 1
|
||||
#define MAX_OFFSET 0xffff
|
||||
#define LITERALS_RUN_LEN 7
|
||||
#define MATCH_RUN_LEN 15
|
||||
|
||||
#define MIN_MATCH_SIZE_V1 3
|
||||
#define LITERALS_RUN_LEN_V1 7
|
||||
#define MATCH_RUN_LEN_V1 15
|
||||
|
||||
#define MIN_MATCH_SIZE_V2 2
|
||||
#define LITERALS_RUN_LEN_V2 3
|
||||
#define MATCH_RUN_LEN_V2 7
|
||||
|
||||
#endif /* _FORMAT_H */
|
||||
|
23
src/frame.c
23
src/frame.c
@ -20,9 +20,18 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "frame.h"
|
||||
#include "shrink.h"
|
||||
|
||||
#define LZSA_ID_0 0x7b
|
||||
#define LZSA_ID_1 0x9e
|
||||
@ -53,11 +62,11 @@ int lzsa_get_frame_size(void) {
|
||||
*
|
||||
* @return number of encoded bytes, or -1 for failure
|
||||
*/
|
||||
int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize) {
|
||||
if (nMaxFrameDataSize >= 3) {
|
||||
int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize, int nFormatVersion) {
|
||||
if (nMaxFrameDataSize >= 3 && (nFormatVersion == 1 || nFormatVersion == 2)) {
|
||||
pFrameData[0] = LZSA_ID_0; /* Magic number */
|
||||
pFrameData[1] = LZSA_ID_1;
|
||||
pFrameData[2] = 0; /* Format version 1 */
|
||||
pFrameData[2] = (nFormatVersion == 2) ? 0x20 : 0; /* Format version 1 */
|
||||
|
||||
return 3;
|
||||
}
|
||||
@ -139,14 +148,16 @@ int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataS
|
||||
*
|
||||
* @return 0 for success, or -1 for failure
|
||||
*/
|
||||
int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize) {
|
||||
int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize, int *nFormatVersion) {
|
||||
if (nFrameDataSize != 3 ||
|
||||
pFrameData[0] != LZSA_ID_0 ||
|
||||
pFrameData[1] != LZSA_ID_1 ||
|
||||
pFrameData[2] != 0) {
|
||||
(pFrameData[2] & 0x1f) != 0 ||
|
||||
((pFrameData[2] & 0xe0) != 0x00 && (pFrameData[2] & 0xe0) != 0x20)) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
*nFormatVersion = (pFrameData[2] & 0xe0) ? 2 : 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
14
src/frame.h
14
src/frame.h
@ -20,6 +20,16 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FRAME_H
|
||||
#define _FRAME_H
|
||||
|
||||
@ -45,7 +55,7 @@ int lzsa_get_frame_size(void);
|
||||
*
|
||||
* @return number of encoded bytes, or -1 for failure
|
||||
*/
|
||||
int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize);
|
||||
int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize, int nFormatVersion);
|
||||
|
||||
/**
|
||||
* Encode compressed block frame header
|
||||
@ -87,7 +97,7 @@ int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataS
|
||||
*
|
||||
* @return 0 for success, or -1 for failure
|
||||
*/
|
||||
int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize);
|
||||
int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize, int *nFormatVersion);
|
||||
|
||||
/**
|
||||
* Decode frame header
|
||||
|
217
src/lib.c
Executable file
217
src/lib.c
Executable file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* lib.c - LZSA library implementation
|
||||
*
|
||||
* Copyright (C) 2019 Emmanuel Marty
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
|
||||
*
|
||||
* Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
|
||||
* With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
|
||||
* With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
|
||||
* Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "lib.h"
|
||||
#include "matchfinder.h"
|
||||
#include "shrink_v1.h"
|
||||
#include "shrink_v2.h"
|
||||
#include "expand_v1.h"
|
||||
#include "expand_v2.h"
|
||||
#include "format.h"
|
||||
|
||||
/**
|
||||
* Initialize compression context
|
||||
*
|
||||
* @param pCompressor compression context to initialize
|
||||
* @param nMaxWindowSize maximum size of input data window (previously compressed bytes + bytes to compress)
|
||||
* @param nMinMatchSize minimum match size (cannot be less than MIN_MATCH_SIZE)
|
||||
* @param nFlags compression flags
|
||||
*
|
||||
* @return 0 for success, non-zero for failure
|
||||
*/
|
||||
int lzsa_compressor_init(lsza_compressor *pCompressor, const int nMaxWindowSize, const int nMinMatchSize, const int nFormatVersion, const int nFlags) {
|
||||
int nResult;
|
||||
int nMinMatchSizeForFormat = (nFormatVersion == 1) ? MIN_MATCH_SIZE_V1 : MIN_MATCH_SIZE_V2;
|
||||
|
||||
nResult = divsufsort_init(&pCompressor->divsufsort_context);
|
||||
pCompressor->intervals = NULL;
|
||||
pCompressor->pos_data = NULL;
|
||||
pCompressor->open_intervals = NULL;
|
||||
pCompressor->match = NULL;
|
||||
pCompressor->best_match = NULL;
|
||||
pCompressor->slot_cost = NULL;
|
||||
pCompressor->repmatch_opt = NULL;
|
||||
pCompressor->min_match_size = nMinMatchSize;
|
||||
if (pCompressor->min_match_size < nMinMatchSizeForFormat)
|
||||
pCompressor->min_match_size = nMinMatchSizeForFormat;
|
||||
else if (pCompressor->min_match_size > 5)
|
||||
pCompressor->min_match_size = 5;
|
||||
pCompressor->format_version = nFormatVersion;
|
||||
pCompressor->flags = nFlags;
|
||||
pCompressor->num_commands = 0;
|
||||
|
||||
if (!nResult) {
|
||||