mirror of
https://github.com/aaru-dps/Aaru.Compression.Native.git
synced 2025-12-16 19:24:31 +00:00
163 lines
3.9 KiB
C
163 lines
3.9 KiB
C
|
|
/*
|
||
|
|
* lzw.c - LZW decompression implementation
|
||
|
|
*
|
||
|
|
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||
|
|
*
|
||
|
|
* This library is free software; you can redistribute it and/or
|
||
|
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
|
* License as published by the Free Software Foundation; either
|
||
|
|
* version 2.1 of the License, or (at your option) any later version.
|
||
|
|
*
|
||
|
|
* This library is distributed in the hope that it will be useful,
|
||
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
|
* Lesser General Public License for more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
|
* License along with this library; if not, write to the Free Software
|
||
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||
|
|
* MA 02110-1301 USA
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "lzw.h"
|
||
|
|
#include <stdlib.h>
|
||
|
|
|
||
|
|
LZW *lzw_alloc(int maxsymbols, int reservedsymbols)
|
||
|
|
{
|
||
|
|
LZW *self = (LZW *)malloc(sizeof(LZW) + sizeof(LZWTreeNode) * maxsymbols);
|
||
|
|
if(!self) return NULL;
|
||
|
|
|
||
|
|
if(maxsymbols < 256 + reservedsymbols)
|
||
|
|
{
|
||
|
|
free(self);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
self->maxsymbols = maxsymbols;
|
||
|
|
self->reservedsymbols = reservedsymbols;
|
||
|
|
|
||
|
|
self->buffer = NULL;
|
||
|
|
self->buffersize = 0;
|
||
|
|
|
||
|
|
for(int i = 0; i < 256; i++)
|
||
|
|
{
|
||
|
|
self->nodes[i].chr = i;
|
||
|
|
self->nodes[i].parent = -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
lzw_clear_table(self);
|
||
|
|
|
||
|
|
return self;
|
||
|
|
}
|
||
|
|
|
||
|
|
void lzw_free(LZW *self)
|
||
|
|
{
|
||
|
|
if(self)
|
||
|
|
{
|
||
|
|
free(self->buffer);
|
||
|
|
free(self);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void lzw_clear_table(LZW *self)
|
||
|
|
{
|
||
|
|
self->numsymbols = 256 + self->reservedsymbols;
|
||
|
|
self->prevsymbol = -1;
|
||
|
|
self->symbolsize = 9; // TODO: technically this depends on reservedsymbols
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint8_t find_first_byte(LZWTreeNode *nodes, int symbol)
|
||
|
|
{
|
||
|
|
while(nodes[symbol].parent >= 0) symbol = nodes[symbol].parent;
|
||
|
|
return nodes[symbol].chr;
|
||
|
|
}
|
||
|
|
|
||
|
|
int lzw_next_symbol(LZW *self, int symbol)
|
||
|
|
{
|
||
|
|
if(self->prevsymbol < 0)
|
||
|
|
{
|
||
|
|
if(symbol >= self->numsymbols) return LZW_INVALID_CODE_ERROR;
|
||
|
|
self->prevsymbol = symbol;
|
||
|
|
return LZW_NO_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
int postfixbyte;
|
||
|
|
if(symbol < self->numsymbols) { postfixbyte = find_first_byte(self->nodes, symbol); }
|
||
|
|
else if(symbol == self->numsymbols) { postfixbyte = find_first_byte(self->nodes, self->prevsymbol); }
|
||
|
|
else { return LZW_INVALID_CODE_ERROR; }
|
||
|
|
|
||
|
|
int parent = self->prevsymbol;
|
||
|
|
self->prevsymbol = symbol;
|
||
|
|
|
||
|
|
if(!lzw_symbol_list_full(self))
|
||
|
|
{
|
||
|
|
self->nodes[self->numsymbols].parent = parent;
|
||
|
|
self->nodes[self->numsymbols].chr = postfixbyte;
|
||
|
|
self->numsymbols++;
|
||
|
|
|
||
|
|
if(!lzw_symbol_list_full(self))
|
||
|
|
{
|
||
|
|
if((self->numsymbols & (self->numsymbols - 1)) == 0) { self->symbolsize++; }
|
||
|
|
}
|
||
|
|
|
||
|
|
return LZW_NO_ERROR;
|
||
|
|
}
|
||
|
|
else { return LZW_TOO_MANY_CODES_ERROR; }
|
||
|
|
}
|
||
|
|
|
||
|
|
int lzw_replace_symbol(LZW *self, int oldsymbol, int symbol)
|
||
|
|
{
|
||
|
|
if(symbol >= self->numsymbols) return LZW_INVALID_CODE_ERROR;
|
||
|
|
|
||
|
|
self->nodes[oldsymbol].parent = self->prevsymbol;
|
||
|
|
self->nodes[oldsymbol].chr = find_first_byte(self->nodes, symbol);
|
||
|
|
|
||
|
|
self->prevsymbol = symbol;
|
||
|
|
|
||
|
|
return LZW_NO_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
int lzw_output_length(LZW *self)
|
||
|
|
{
|
||
|
|
int symbol = self->prevsymbol;
|
||
|
|
int n = 0;
|
||
|
|
|
||
|
|
while(symbol >= 0)
|
||
|
|
{
|
||
|
|
symbol = self->nodes[symbol].parent;
|
||
|
|
n++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
int lzw_output_to_buffer(LZW *self, uint8_t *buffer)
|
||
|
|
{
|
||
|
|
int symbol = self->prevsymbol;
|
||
|
|
int n = lzw_output_length(self);
|
||
|
|
buffer += n;
|
||
|
|
|
||
|
|
while(symbol >= 0)
|
||
|
|
{
|
||
|
|
*--buffer = self->nodes[symbol].chr;
|
||
|
|
symbol = self->nodes[symbol].parent;
|
||
|
|
}
|
||
|
|
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
int lzw_reverse_output_to_buffer(LZW *self, uint8_t *buffer)
|
||
|
|
{
|
||
|
|
int symbol = self->prevsymbol;
|
||
|
|
int n = 0;
|
||
|
|
|
||
|
|
while(symbol >= 0)
|
||
|
|
{
|
||
|
|
*buffer++ = self->nodes[symbol].chr;
|
||
|
|
symbol = self->nodes[symbol].parent;
|
||
|
|
n++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return n;
|
||
|
|
}
|