mirror of
https://github.com/emmanuel-marty/lzsa.git
synced 2025-02-12 22:31:40 +00:00
Reorganize token byte for faster decoding on 8-bit CPUs, without affecting the compression ratio
This commit is contained in:
parent
33b62c004a
commit
1ef1ad8111
@ -75,7 +75,7 @@ Each frame contains a 3-bytes length followed by block data that expands to up t
|
||||
|
||||
LZSA blocks are composed from consecutive commands. Each command follows this format:
|
||||
|
||||
* token: <O|LLL|MMMM>
|
||||
* token: <LLL|MMMM|O>
|
||||
* optional extra literal length
|
||||
* literal values
|
||||
* match offset low
|
||||
@ -87,11 +87,11 @@ LZSA blocks are composed from consecutive commands. Each command follows this fo
|
||||
The token byte is broken down into three parts:
|
||||
|
||||
7 6 5 4 3 2 1 0
|
||||
O L L L M M M M
|
||||
L L L M M M M O
|
||||
|
||||
* O: set for a 2-bytes match offset, clear for a 1-byte match offset
|
||||
* L: 3-bit literals length (0-6, or 7 if extended). If the number of literals for this command is 0 to 6, the length is encoded in the token and no extra bytes are required. Otherwise, a value of 7 is encoded and extra bytes follow as 'optional extra literal length'
|
||||
* M: 4-bit encoded match length (0-14, or 15 if extended). Likewise, if the encoded match length for this command is 0 to 14, it is directly stored, otherwise 15 is stored and extra bytes follow as 'optional extra encoded match length'. Except for the last command in a block, a command always contains a match, so the encoded match length is the actual match length offset by the minimum, which is 3 bytes. For instance, an actual match length of 10 bytes to be copied, is encoded as 7.
|
||||
* O: set for a 2-bytes match offset, clear for a 1-byte match offset
|
||||
|
||||
**optional extra literal length**
|
||||
|
||||
@ -113,7 +113,7 @@ The low 8 bits of the match offset follows.
|
||||
|
||||
**optional match offset high**
|
||||
|
||||
If the 'O' bit (bit 7) is set in the token, the high 8 bits of the match offset follow, otherwise they are understood to be all set to 0.
|
||||
If the 'O' bit (bit 0) is set in the token, the high 8 bits of the match offset follow, otherwise they are understood to be all set to 0.
|
||||
|
||||
**important note regarding short match offsets: off by 1**
|
||||
|
||||
|
@ -37,13 +37,13 @@ lzsa_decompress:
|
||||
xor cx,cx
|
||||
|
||||
.decode_token:
|
||||
mov ax,cx ; clear ah - cx is zero from above or from after rep movsw in .copy_match
|
||||
lodsb ; read token byte: O|LLL|MMMM
|
||||
mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match
|
||||
lodsb ; read token byte: LLL|MMMM|O
|
||||
mov dx,ax ; keep token in dl
|
||||
|
||||
and al,070H ; isolate literals length in token (LLL)
|
||||
mov cl,4
|
||||
shr al,cl ; shift match length into place
|
||||
mov cl,3
|
||||
rol al,cl ; shift literals length into place
|
||||
and al,07H ; isolate literals length in token (LLL)
|
||||
|
||||
mov cx,ax ; copy literals length into cx
|
||||
cmp al,07H ; LITERALS_RUN_LEN?
|
||||
@ -54,10 +54,10 @@ lzsa_decompress:
|
||||
.copy_literals:
|
||||
rep movsb ; copy cx literals from ds:si to es:di
|
||||
|
||||
test dl,dl ; check match offset size in token (O bit, ie. bit 7, the sign bit)
|
||||
js .get_long_offset
|
||||
ror dl,1 ; check match offset size in token (O bit)
|
||||
jc .get_long_offset
|
||||
|
||||
xor ah,ah ; Get 1-byte match offset
|
||||
xchg ax,cx ; clear ah - cx is zero from the rep movsb above
|
||||
lodsb
|
||||
inc ax ; the match offset is stored off-by-1, increase it
|
||||
jmp short .get_match_length
|
||||
|
12
src/expand.c
12
src/expand.c
@ -168,7 +168,7 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
|
||||
while (pInBlock < pInBlockFastEnd && pCurOutData < pOutDataFastEnd) {
|
||||
const unsigned char token = *pInBlock++;
|
||||
int nLiterals = (int)((unsigned int)((token & 0x70) >> 4));
|
||||
int nLiterals = (int)((unsigned int)((token & 0xe0) >> 5));
|
||||
|
||||
if (nLiterals < LITERALS_RUN_LEN) {
|
||||
memcpy(pCurOutData, pInBlock, 8);
|
||||
@ -184,7 +184,7 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
int nMatchOffset;
|
||||
|
||||
nMatchOffset = ((unsigned int)*pInBlock++);
|
||||
if (token & 0x80) {
|
||||
if (token & 0x01) {
|
||||
if (pInBlock >= pInBlockEnd) return -1;
|
||||
nMatchOffset |= (((unsigned int)*pInBlock++) << 8);
|
||||
if (nMatchOffset == 0) break;
|
||||
@ -197,7 +197,7 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
if (pSrc < pOutData)
|
||||
return -1;
|
||||
|
||||
int nMatchLen = (int)((unsigned int)(token & 0x0f));
|
||||
int nMatchLen = (int)((unsigned int)((token & 0x1e) >> 1));
|
||||
if (nMatchLen < (16 - MIN_MATCH_SIZE + 1) && (pSrc + MIN_MATCH_SIZE + nMatchLen) < pCurOutData && pCurOutData < pOutDataFastEnd) {
|
||||
memcpy(pCurOutData, pSrc, 16);
|
||||
pCurOutData += (MIN_MATCH_SIZE + nMatchLen);
|
||||
@ -213,7 +213,7 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
|
||||
while (pInBlock < pInBlockEnd) {
|
||||
const unsigned char token = *pInBlock++;
|
||||
int nLiterals = (int)((unsigned int)((token & 0x70) >> 4));
|
||||
int nLiterals = (int)((unsigned int)((token & 0xe0) >> 5));
|
||||
|
||||
if (lzsa_expand_literals_slow(&pInBlock, pInBlockEnd, nLiterals, &pCurOutData, pOutDataEnd))
|
||||
return -1;
|
||||
@ -222,7 +222,7 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
int nMatchOffset;
|
||||
|
||||
nMatchOffset = ((unsigned int)*pInBlock++);
|
||||
if (token & 0x80) {
|
||||
if (token & 0x01) {
|
||||
if (pInBlock >= pInBlockEnd) return -1;
|
||||
nMatchOffset |= (((unsigned int)*pInBlock++) << 8);
|
||||
if (nMatchOffset == 0) break;
|
||||
@ -235,7 +235,7 @@ int lzsa_expand_block(const unsigned char *pInBlock, int nBlockSize, unsigned ch
|
||||
if (pSrc < pOutData)
|
||||
return -1;
|
||||
|
||||
int nMatchLen = (int)((unsigned int)(token & 0x0f));
|
||||
int nMatchLen = (int)((unsigned int)((token & 0x1e) >> 1));
|
||||
if (lzsa_expand_match_slow(&pInBlock, pInBlockEnd, pSrc, nMatchLen, &pCurOutData, pOutDataEnd, pOutDataFastEnd))
|
||||
return -1;
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ static int lzsa_write_block(lsza_compressor *pCompressor, const unsigned char *p
|
||||
int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE;
|
||||
int nNibbleLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN) ? LITERALS_RUN_LEN : nNumLiterals;
|
||||
int nNibbleMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN) ? MATCH_RUN_LEN : nEncodedMatchLen;
|
||||
int nNibbleLongOffset = (nMatchOffset <= 256) ? 0x00 : 0x80;
|
||||
int nNibbleLongOffset = (nMatchOffset <= 256) ? 0x00 : 0x01;
|
||||
int nTokenSize = 1 /* nibble */ + lzsa_get_literals_varlen_size(nNumLiterals) + nNumLiterals + (nNibbleLongOffset ? 2 : 1) /* match offset */ + lzsa_get_match_varlen_size(nEncodedMatchLen);
|
||||
|
||||
if ((nOutOffset + nTokenSize) > nMaxOutDataSize)
|
||||
@ -593,7 +593,7 @@ static int lzsa_write_block(lsza_compressor *pCompressor, const unsigned char *p
|
||||
if (nMatchOffset < MIN_OFFSET || nMatchOffset > MAX_OFFSET)
|
||||
return -1;
|
||||
|
||||
pOutData[nOutOffset++] = nNibbleLongOffset | (nNibbleLiteralsLen << 4) | nNibbleMatchLen;
|
||||
pOutData[nOutOffset++] = (nNibbleLiteralsLen << 5) | (nNibbleMatchLen << 1) | nNibbleLongOffset;
|
||||
nOutOffset = lzsa_write_literals_varlen(pOutData, nOutOffset, nNumLiterals);
|
||||
|
||||
if (nNumLiterals != 0) {
|
||||
@ -627,7 +627,7 @@ static int lzsa_write_block(lsza_compressor *pCompressor, const unsigned char *p
|
||||
if ((nOutOffset + nTokenSize) > nMaxOutDataSize)
|
||||
return -1;
|
||||
|
||||
pOutData[nOutOffset++] = 0x80 | (nNibbleLiteralsLen << 4);
|
||||
pOutData[nOutOffset++] = (nNibbleLiteralsLen << 5) | 0x01;
|
||||
nOutOffset = lzsa_write_literals_varlen(pOutData, nOutOffset, nNumLiterals);
|
||||
|
||||
if (nNumLiterals != 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user