* harbour/common.mak
* harbour/include/hbapirdd.h
- harbour/include/hbrddwrk.h
* harbour/source/rdd/Makefile
+ harbour/source/rdd/wacore.c
+ harbour/source/rdd/wafunc.c
* harbour/source/rdd/workarea.c
* cleaned RDD code:
* the default work area implementation from which each RDD inherits
and RDD management is now in workarea.c file - this part will be
common for all threads in future MT version
* the code to mange workareas and aliases moved to wacore.c - this
code uses some static variables which will be moved to HVM stack
in MT version
* helper RDD functions thread independent moved to wafunc.c
* harbour/source/rdd/dbcmd.c
* removed RDD/WorkArea management code - only independent HB_FUNCs
left. IMHO it will be good to divide this file into few ones.
* changed dbCreate() and dbUseArea() to return logical value
* harbour/config/global.cf
- removed nulsys from linked library list - it should not be here
because it can confuse some linkers and wrong library will be
created
* harbour/contrib/rdd_ads/ads1.c
* harbour/source/rdd/dbf1.c
* harbour/source/rdd/delim1.c
* harbour/source/rdd/sdf1.c
* harbour/source/rdd/dbfcdx/dbfcdx1.c
* harbour/source/rdd/dbfdbt/dbfdbt1.c
* harbour/source/rdd/dbffpt/dbffpt1.c
* harbour/source/rdd/dbfntx/dbfntx1.c
* harbour/source/rdd/nulsys/nulsys.c
* updated for recent modifications in RDD API
* harbour/include/hbextern.ch
- removed __RDDSETDEFAULT() - this function was necessary with old
rdd register code and we not not need it for few years. It's not
Clipper function so I do not see any reason to keep it but if you
want we can add wrapper to standard RDDSETDEFAULT()
* harbour/source/rdd/hbsix/sxcompr.c
+ added workaround for SIX3 bug which I intentionally replicated in
[x]Harbour but it may cause data corruption so IMHO it will be better
to fix it.
* harbour/source/rtl/errorsys.prg
* added default action for EG_LOCK - it's Clipper compatible behavior
but done in differ way. Due to some differences in linker we are
using we cannot exactly replicate Clipper behavior because it will
not work on some platforms as expected so I decided to add it here.
I think it's even more Clear then the trick with source/sys/ntxerr.prg
done by Clipper. Many people do not even knows about it.
* harbour/source/rtl/gtcrs/chrmap.c
* changed environment variable name from HB_CHRMAP to HB_CHARMAP to
be compatible with description in default map file
748 lines
24 KiB
C
748 lines
24 KiB
C
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* xHarbour Project source code:
|
|
* SIX compatible functions:
|
|
* hb_LZSSxCompressMem()
|
|
* hb_LZSSxDecompressMem()
|
|
* hb_LZSSxCompressFile()
|
|
* hb_LZSSxDecompressFile()
|
|
*
|
|
* SX_FCOMPRESS
|
|
* SX_FDECOMPRESS
|
|
* _SX_STRDECOMPRESS
|
|
* _SX_STRCOMPRESS
|
|
*
|
|
* Copyright 2005 Przemyslaw Czerpak <druzus@acn.waw.pl>
|
|
* www - http://www.xharbour.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
SIX uses modified version of LZSS algorithm.
|
|
It uses 12 bits for position offset in ring buffer and 4 bits for the
|
|
match length. LZSS is modified version of LZ77 algorithm which can store
|
|
original bytes from input string instead of position and match length in
|
|
the ring buffer when the match length does not reach some limit. In SIX
|
|
the minimum match length is 3 and it is used to increase to match length
|
|
in 4 bit offset by adding 3 also so the efective maximum match length is
|
|
18. Of course we have to store the information about the type of item in
|
|
compressed data to know it is (offset+length) pair or simple byte.
|
|
SIX put 1 byte in compressed data which inform about the type of next
|
|
8 items: bit set to 1 means normal byte, 0 means item pair.
|
|
The funny thing is that SIX seems to effectively use only 11 bits ring
|
|
buffer (2048 bytes size). I still do not know why. Maybe it was a hack
|
|
for some problems with implementation of encoding or work around for
|
|
a bug in some signed/unsigned value conversions? Or maybe someone
|
|
wanted to reduce memory overhead in used algorithm for finding the
|
|
longest match? The SIX was written for 16-bit DOS and the memory
|
|
consumption was important though it should not be too much. They
|
|
documented ~9KB increasing the ring buffer size should be linear to
|
|
other used (helper) structures.
|
|
The next interesting thing is that it dynamically overwrites the ring
|
|
buffer with stream data and does not use any separate look ahead buffers.
|
|
This fact suggests that SIX uses some simple implementation with
|
|
greedy early parsing and does not look for optimal matches.
|
|
Our algorithm has to make the same with the ring buffer to be compatible.
|
|
UPDATE: Using smaller ring buffer without increasing the match pointer
|
|
suggested me that it is possible that someone didn't understand it fully
|
|
and modified already existing algorithm. I spend a while with a google
|
|
looking for old LZSS implementations and I've found it. IMHO in 99%
|
|
this code is used in SIX. This is lzss.c file written by Haruhiko Okumura
|
|
with the following note in header:
|
|
,---------------------------------------------------------
|
|
| Use, distribute, and modify this program freely.
|
|
| Please send me your improved versions.
|
|
`---------------------------------------------------------
|
|
This LZSS implementation gives exactly the same results as SIX when we
|
|
change the ring buffer size (#define N 4096 //in the head of this file)
|
|
to 2048. So I used exactly the same algorithm with binary tree for
|
|
finding the longest match. The code may looks a little bit differ then
|
|
in lzss.c because I've already implement most of it before I found
|
|
this file and I didn't want to remove it but the original author is
|
|
Haruhiko Okumura. The text above allow us use it in [x]Harbour.
|
|
This version isn't for sure improved due to SIX modifications so I do
|
|
not think I should send it to him. IMHO it is also less usable for
|
|
other things then strict SIX compatibility - we already have much
|
|
stronger compressions in ZLIB.
|
|
After the LZSS compression the result could be longer then original
|
|
strings. It looks that SIX return original string when the compressed
|
|
one is longer then original + 253. I do not like it because there is
|
|
no marker which informs if string was compressed or not and calling
|
|
decompression procedure for uncompressed data gives broken results.
|
|
I do not want to make the same.
|
|
When file is compressed then SIX put the uncompressed file size in
|
|
first four bytes. Similar situation is with string returned by
|
|
sx_compress() - first four bytes is uncompressed size in little
|
|
endian order. This has to be done in upper level functions,
|
|
hb_LZSSxCompressMem() and hb_LZSSxCompressFile() intentionally
|
|
do not do that.
|
|
|
|
*/
|
|
|
|
#include "hbsxfunc.h"
|
|
|
|
#define HB_SX_UNCOMPRESED 0xFFFFFFFFUL
|
|
|
|
|
|
/* number of bits for encoded item (position,length) */
|
|
#define ITEMBITS 16
|
|
/* unused DUMMY bits - who does know why SIX has it? */
|
|
#define DUMMYBITS 1
|
|
/* number of bits for position offset */
|
|
#define OFFSETBITS (12 - DUMMYBITS)
|
|
/* the minimum match length to encode as new position */
|
|
#define MINLENGTH 3
|
|
|
|
/* number of bits for match length, 1 bit reserved for ITEM type */
|
|
#define LENGTHBITS ( ITEMBITS - OFFSETBITS - DUMMYBITS )
|
|
/* the maximum match length we can encode in LENGTHBITS */
|
|
#define MAXLENGTH ( ( 1 << LENGTHBITS ) + MINLENGTH - 1 )
|
|
/* size of ring buffer */
|
|
#define RBUFLENGTH ( 1 << OFFSETBITS )
|
|
/* the bit mask for ring buffer */
|
|
#define RBUFMASK ( ( 1 << OFFSETBITS ) - 1 )
|
|
/* the bit mask for match length */
|
|
#define MATCHMASK ( ( 1 << LENGTHBITS ) - 1 )
|
|
/* get ringbuffer index */
|
|
#define RBUFINDEX(i) ( ( i ) & RBUFMASK )
|
|
|
|
/* get ring buffer offset position from low and high bytes */
|
|
#define LZSS_OFFSET(l, h) ( (l) | ( (h & ~MATCHMASK) << ( 8 - LENGTHBITS ) ) )
|
|
/* get match length from low and high byte */
|
|
#define LZSS_LENGTH(l, h) ( ( (h) & MATCHMASK ) + MINLENGTH )
|
|
|
|
/* create compressed item from match position and length */
|
|
#define LZSS_ITEM(o, l) ( ( (o) << LENGTHBITS ) | ( (l) - MINLENGTH ) )
|
|
/* create low byte of compressed item */
|
|
#define LZSS_ITMLO(o, l) ( ( BYTE ) (o) )
|
|
/* create high byte of compressed item */
|
|
#define LZSS_ITMHI(o, l) ( ( BYTE ) ( ( ( (o) >> ( 8 - LENGTHBITS ) ) & ~MATCHMASK ) | \
|
|
( (l) - MINLENGTH ) ) )
|
|
/* maximum size of item set: byte with item type bits plus 8 items */
|
|
#define ITEMSETSIZE ( ( ITEMBITS << 3 ) + 1 )
|
|
|
|
/* the size of IO buffer for file (de)compression */
|
|
#define LZSS_IOBUFLEN 8192
|
|
|
|
/* uninitialized (dummy) node in compression trees */
|
|
#define DUMMYNODE RBUFLENGTH
|
|
|
|
|
|
typedef struct _HB_LZSSX_COMPR
|
|
{
|
|
FHANDLE hInput;
|
|
BYTE * inBuffer;
|
|
ULONG inBuffSize;
|
|
ULONG inBuffPos;
|
|
ULONG inBuffRead;
|
|
BOOL fInFree;
|
|
|
|
FHANDLE hOutput;
|
|
BYTE * outBuffer;
|
|
ULONG outBuffSize;
|
|
ULONG outBuffPos;
|
|
BOOL fOutFree;
|
|
|
|
ULONG ulMaxSize;
|
|
ULONG ulOutSize;
|
|
BOOL fResult;
|
|
BOOL fContinue;
|
|
|
|
BYTE ring_buffer[ RBUFLENGTH + MAXLENGTH - 1 ];
|
|
|
|
SHORT match_offset;
|
|
SHORT match_length;
|
|
SHORT parent[ RBUFLENGTH + 1 ];
|
|
SHORT left [ RBUFLENGTH + 1 ];
|
|
SHORT right [ RBUFLENGTH + 257 ];
|
|
}
|
|
HB_LZSSX_COMPR;
|
|
typedef HB_LZSSX_COMPR * PHB_LZSSX_COMPR;
|
|
|
|
static void hb_LZSSxExit( PHB_LZSSX_COMPR pCompr )
|
|
{
|
|
if( pCompr->fInFree )
|
|
hb_xfree( pCompr->inBuffer );
|
|
if( pCompr->fOutFree )
|
|
hb_xfree( pCompr->outBuffer );
|
|
hb_xfree( pCompr );
|
|
}
|
|
|
|
static PHB_LZSSX_COMPR hb_LZSSxInit(
|
|
FHANDLE hInput, BYTE * pSrcBuf, ULONG ulSrcBuf,
|
|
FHANDLE hOutput, BYTE * pDstBuf, ULONG ulDstBuf )
|
|
{
|
|
PHB_LZSSX_COMPR pCompr = ( PHB_LZSSX_COMPR ) hb_xgrab( sizeof( HB_LZSSX_COMPR ) );
|
|
|
|
if( hInput != FS_ERROR && ulSrcBuf == 0 )
|
|
ulSrcBuf = LZSS_IOBUFLEN;
|
|
if( hOutput != FS_ERROR && ulDstBuf == 0 )
|
|
ulDstBuf = LZSS_IOBUFLEN;
|
|
|
|
pCompr->hInput = hInput;
|
|
pCompr->inBuffer = pSrcBuf;
|
|
pCompr->inBuffSize = ulSrcBuf;
|
|
pCompr->inBuffPos = 0;
|
|
pCompr->inBuffRead = ( hInput == FS_ERROR ) ? ulSrcBuf : 0;
|
|
pCompr->fInFree = ( hInput != FS_ERROR && pSrcBuf == NULL );
|
|
pCompr->hOutput = hOutput;
|
|
pCompr->outBuffer = pDstBuf;
|
|
pCompr->outBuffSize = ulDstBuf;
|
|
pCompr->outBuffPos = 0;
|
|
pCompr->fOutFree = ( hOutput != FS_ERROR && pDstBuf == NULL );
|
|
|
|
pCompr->ulMaxSize = 0;
|
|
pCompr->ulOutSize = 0;
|
|
pCompr->fResult = TRUE;
|
|
pCompr->fContinue = FALSE;
|
|
|
|
if( pCompr->fInFree )
|
|
pCompr->inBuffer = ( BYTE * ) hb_xgrab( ulDstBuf );
|
|
if( pCompr->fOutFree )
|
|
pCompr->outBuffer = ( BYTE * ) hb_xgrab( ulDstBuf );
|
|
|
|
/* initialize the ring buffer with spaces, because SIX uses
|
|
dynamic ring buffer then we do not have to fill last MAXLENGTH
|
|
characters */
|
|
memset( pCompr->ring_buffer, ' ', RBUFLENGTH - MAXLENGTH );
|
|
|
|
return pCompr;
|
|
}
|
|
|
|
static BOOL hb_LZSSxFlush( PHB_LZSSX_COMPR pCompr )
|
|
{
|
|
if( pCompr->fResult && pCompr->hOutput != FS_ERROR )
|
|
{
|
|
if( hb_fsWriteLarge( pCompr->hOutput, pCompr->outBuffer,
|
|
pCompr->outBuffPos ) != pCompr->outBuffPos )
|
|
{
|
|
pCompr->fResult = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pCompr->ulOutSize += pCompr->outBuffPos;
|
|
pCompr->outBuffPos = 0;
|
|
}
|
|
}
|
|
return pCompr->fResult;
|
|
}
|
|
|
|
static BOOL hb_LZSSxWrite( PHB_LZSSX_COMPR pCompr, BYTE bVal )
|
|
{
|
|
if( pCompr->fResult )
|
|
{
|
|
if( pCompr->outBuffPos == pCompr->outBuffSize )
|
|
hb_LZSSxFlush( pCompr );
|
|
if( pCompr->outBuffPos < pCompr->outBuffSize )
|
|
pCompr->outBuffer[pCompr->outBuffPos] = bVal;
|
|
else
|
|
pCompr->fResult = FALSE;
|
|
}
|
|
pCompr->outBuffPos++;
|
|
return pCompr->fResult || pCompr->fContinue;
|
|
}
|
|
|
|
static int hb_LZSSxRead( PHB_LZSSX_COMPR pCompr )
|
|
{
|
|
if( pCompr->inBuffPos < pCompr->inBuffRead )
|
|
return pCompr->inBuffer[ pCompr->inBuffPos++ ];
|
|
|
|
if( pCompr->hInput != FS_ERROR )
|
|
{
|
|
pCompr->inBuffRead = hb_fsReadLarge( pCompr->hInput, pCompr->inBuffer,
|
|
pCompr->inBuffSize );
|
|
pCompr->inBuffPos = 0;
|
|
if( pCompr->inBuffPos < pCompr->inBuffRead )
|
|
return pCompr->inBuffer[ pCompr->inBuffPos++ ];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static BOOL hb_LZSSxDecode( PHB_LZSSX_COMPR pCompr )
|
|
{
|
|
BOOL fResult = TRUE;
|
|
USHORT itemMask;
|
|
int offset, length, index, c, h;
|
|
|
|
index = RBUFLENGTH - MAXLENGTH;
|
|
itemMask = 0;
|
|
|
|
do
|
|
{
|
|
itemMask >>= 1;
|
|
/* Is the next character bitfield with type of next 8 items ? */
|
|
if( ( itemMask & 0x0100 ) == 0 )
|
|
{
|
|
if( ( c = hb_LZSSxRead( pCompr ) ) == -1 )
|
|
break;
|
|
/* simple trick to reduce number of shift operations */
|
|
itemMask = c | 0xff00;
|
|
}
|
|
if( ( c = hb_LZSSxRead( pCompr ) ) == -1 )
|
|
break;
|
|
|
|
if( itemMask & 1 ) /* Is the next character normal byte ? */
|
|
{
|
|
if( ! hb_LZSSxWrite( pCompr, ( BYTE ) c ) )
|
|
{
|
|
fResult = FALSE;
|
|
break;
|
|
}
|
|
pCompr->ring_buffer[ index ] = ( BYTE ) c;
|
|
index = RBUFINDEX( index + 1 );
|
|
}
|
|
else /* we have an item pair (ring buffer offset : match length) */
|
|
{
|
|
if( ( h = hb_LZSSxRead( pCompr ) ) == -1 )
|
|
{
|
|
/* fResult = FALSE; */
|
|
break;
|
|
}
|
|
offset = LZSS_OFFSET( c, h ); /* get offset to ring buffer */
|
|
length = LZSS_LENGTH( c, h ); /* get match length */
|
|
for( h = 0; h < length; h++ )
|
|
{
|
|
c = pCompr->ring_buffer[ RBUFINDEX( offset + h ) ];
|
|
if( ! hb_LZSSxWrite( pCompr, ( BYTE ) c ) )
|
|
{
|
|
fResult = FALSE;
|
|
break;
|
|
}
|
|
/* SIX does not use additional buffers and dynamically
|
|
overwrite the ring buffer - we have to make exactly
|
|
the same or our results will be differ when
|
|
abs( offset - index ) < length */
|
|
pCompr->ring_buffer[ index ] = ( BYTE ) c;
|
|
index = RBUFINDEX( index + 1 );
|
|
}
|
|
}
|
|
}
|
|
while( fResult );
|
|
|
|
if( fResult )
|
|
fResult = hb_LZSSxFlush( pCompr );
|
|
return fResult;
|
|
}
|
|
|
|
static void hb_LZSSxNodeInsert( PHB_LZSSX_COMPR pCompr, int r )
|
|
{
|
|
int i, p, cmp;
|
|
unsigned char *key;
|
|
|
|
cmp = 1;
|
|
key = &pCompr->ring_buffer[ r ];
|
|
p = RBUFLENGTH + 1 + key[ 0 ];
|
|
pCompr->right[ r ] = pCompr->left[ r ] = DUMMYNODE;
|
|
pCompr->match_length = 0;
|
|
|
|
for( ; ; )
|
|
{
|
|
if( cmp >= 0 )
|
|
{
|
|
if( pCompr->right[ p ] != DUMMYNODE )
|
|
p = pCompr->right[ p ];
|
|
else
|
|
{
|
|
pCompr->right[ p ] = r;
|
|
pCompr->parent[ r ] = p;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pCompr->left[ p ] != DUMMYNODE )
|
|
p = pCompr->left[ p ];
|
|
else
|
|
{
|
|
pCompr->left[ p ] = r;
|
|
pCompr->parent[ r ] = p;
|
|
return;
|
|
}
|
|
}
|
|
for( i = 1; i < MAXLENGTH; i++ )
|
|
{
|
|
if( ( cmp = key[ i ] - pCompr->ring_buffer[ p + i ] ) != 0 )
|
|
break;
|
|
}
|
|
if( i > pCompr->match_length )
|
|
{
|
|
pCompr->match_offset = p;
|
|
pCompr->match_length = i;
|
|
if( i >= MAXLENGTH )
|
|
break;
|
|
}
|
|
}
|
|
pCompr->parent[ r ] = pCompr->parent[ p ];
|
|
pCompr->left[ r ] = pCompr->left[ p ];
|
|
pCompr->right[ r ] = pCompr->right[ p ];
|
|
pCompr->parent[ pCompr->left[ p ] ] = r;
|
|
pCompr->parent[ pCompr->right[ p ] ] = r;
|
|
if( pCompr->right[ pCompr->parent[ p ] ] == p )
|
|
pCompr->right[ pCompr->parent[ p ] ] = r;
|
|
else
|
|
pCompr->left[ pCompr->parent[ p ] ] = r;
|
|
pCompr->parent[ p ] = DUMMYNODE;
|
|
}
|
|
|
|
static void hb_LZSSxNodeDelete( PHB_LZSSX_COMPR pCompr, int p )
|
|
{
|
|
if( pCompr->parent[ p ] != DUMMYNODE )
|
|
{
|
|
int q;
|
|
if( pCompr->right[ p ] == DUMMYNODE )
|
|
q = pCompr->left[ p ];
|
|
else if( pCompr->left[ p ] == DUMMYNODE )
|
|
q = pCompr->right[ p ];
|
|
else
|
|
{
|
|
q = pCompr->left[ p ];
|
|
if( pCompr->right[ q ] != DUMMYNODE )
|
|
{
|
|
do
|
|
{
|
|
q = pCompr->right[ q ];
|
|
}
|
|
while( pCompr->right[ q ] != DUMMYNODE );
|
|
pCompr->right[ pCompr->parent[ q ] ] = pCompr->left[ q ];
|
|
pCompr->parent[ pCompr->left[ q ] ] = pCompr->parent[ q ];
|
|
pCompr->left[ q ] = pCompr->left[ p ];
|
|
pCompr->parent[ pCompr->left[ p ] ] = q;
|
|
}
|
|
pCompr->right[ q ] = pCompr->right[ p ];
|
|
pCompr->parent[ pCompr->right[ p ] ] = q;
|
|
}
|
|
pCompr->parent[ q ] = pCompr->parent[ p ];
|
|
if( pCompr->right[ pCompr->parent[ p ] ] == p )
|
|
pCompr->right[ pCompr->parent[ p ] ] = q;
|
|
else
|
|
pCompr->left[ pCompr->parent[ p ] ] = q;
|
|
pCompr->parent[ p ] = DUMMYNODE;
|
|
}
|
|
}
|
|
|
|
static ULONG hb_LZSSxEncode( PHB_LZSSX_COMPR pCompr )
|
|
{
|
|
BYTE itemSet[ITEMSETSIZE];
|
|
BYTE itemMask;
|
|
ULONG ulSize = 0;
|
|
int iItem;
|
|
short int i, c, len, r, s, last_match_length;
|
|
|
|
for( i = RBUFLENGTH + 1; i < RBUFLENGTH + 257; i++ )
|
|
pCompr->right[i] = DUMMYNODE;
|
|
for( i = 0; i < RBUFLENGTH; i++ )
|
|
pCompr->parent[ i ] = DUMMYNODE;
|
|
|
|
itemSet[ 0 ] = 0;
|
|
iItem = itemMask = 1;
|
|
s = 0;
|
|
r = RBUFLENGTH - MAXLENGTH;
|
|
|
|
for( len = 0; len < MAXLENGTH; len++ )
|
|
{
|
|
if( ( c = hb_LZSSxRead( pCompr ) ) == -1 )
|
|
break;
|
|
pCompr->ring_buffer[ r + len ] = ( BYTE ) c;
|
|
}
|
|
if( len == 0 )
|
|
return ulSize;
|
|
|
|
for( i = 1; i <= MAXLENGTH; i++ )
|
|
hb_LZSSxNodeInsert( pCompr, r - i );
|
|
hb_LZSSxNodeInsert( pCompr, r );
|
|
|
|
do
|
|
{
|
|
if( pCompr->match_length > len )
|
|
pCompr->match_length = len;
|
|
if( pCompr->match_length < MINLENGTH )
|
|
{
|
|
pCompr->match_length = 1;
|
|
itemSet[ 0 ] |= itemMask;
|
|
itemSet[ iItem++ ] = pCompr->ring_buffer[ r ];
|
|
}
|
|
else
|
|
{
|
|
itemSet[iItem++] = LZSS_ITMLO( pCompr->match_offset,
|
|
pCompr->match_length );
|
|
itemSet[iItem++] = LZSS_ITMHI( pCompr->match_offset,
|
|
pCompr->match_length );
|
|
}
|
|
if( ( itemMask <<= 1 ) == 0 )
|
|
{
|
|
for( i = 0; i < iItem; i++ )
|
|
{
|
|
if( !hb_LZSSxWrite( pCompr, itemSet[ i ] ) )
|
|
return ( ULONG ) -1;
|
|
}
|
|
ulSize += iItem;
|
|
itemSet[ 0 ] = 0;
|
|
iItem = itemMask = 1;
|
|
}
|
|
last_match_length = pCompr->match_length;
|
|
for( i = 0; i < last_match_length &&
|
|
( c = hb_LZSSxRead( pCompr ) ) != -1; i++ )
|
|
{
|
|
hb_LZSSxNodeDelete( pCompr, s );
|
|
pCompr->ring_buffer[ s ] = ( BYTE ) c;
|
|
if( s < MAXLENGTH - 1 )
|
|
pCompr->ring_buffer[ s + RBUFLENGTH ] = ( BYTE ) c;
|
|
s = RBUFINDEX( s + 1 );
|
|
r = RBUFINDEX( r + 1 );
|
|
hb_LZSSxNodeInsert( pCompr, r );
|
|
}
|
|
while( i++ < last_match_length )
|
|
{
|
|
hb_LZSSxNodeDelete( pCompr, s );
|
|
s = RBUFINDEX( s + 1 );
|
|
r = RBUFINDEX( r + 1 );
|
|
if( --len )
|
|
hb_LZSSxNodeInsert( pCompr, r );
|
|
}
|
|
} while( len > 0 );
|
|
|
|
if( iItem > 1 )
|
|
{
|
|
for( i = 0; i < iItem; i++ )
|
|
{
|
|
if( !hb_LZSSxWrite( pCompr, itemSet[ i ] ) )
|
|
return ( ULONG ) -1;
|
|
}
|
|
ulSize += iItem;
|
|
}
|
|
|
|
if( !hb_LZSSxFlush( pCompr ) )
|
|
return ( ULONG ) -1;
|
|
|
|
return ulSize;
|
|
}
|
|
|
|
|
|
BOOL hb_LZSSxCompressMem( BYTE * pSrcBuf, ULONG ulSrcLen,
|
|
BYTE * pDstBuf, ULONG ulDstLen,
|
|
ULONG * pulSize )
|
|
{
|
|
PHB_LZSSX_COMPR pCompr;
|
|
ULONG ulSize;
|
|
|
|
pCompr = hb_LZSSxInit( FS_ERROR, pSrcBuf, ulSrcLen,
|
|
FS_ERROR, pDstBuf, ulDstLen );
|
|
ulSize = hb_LZSSxEncode( pCompr );
|
|
hb_LZSSxExit( pCompr );
|
|
if( pulSize )
|
|
*pulSize = ulSize;
|
|
return ( ulSize <= ulDstLen );
|
|
}
|
|
|
|
BOOL hb_LZSSxDecompressMem( BYTE * pSrcBuf, ULONG ulSrcLen,
|
|
BYTE * pDstBuf, ULONG ulDstLen )
|
|
{
|
|
PHB_LZSSX_COMPR pCompr;
|
|
BOOL fResult;
|
|
|
|
pCompr = hb_LZSSxInit( FS_ERROR, pSrcBuf, ulSrcLen,
|
|
FS_ERROR, pDstBuf, ulDstLen );
|
|
fResult = hb_LZSSxDecode( pCompr );
|
|
hb_LZSSxExit( pCompr );
|
|
return fResult;
|
|
}
|
|
|
|
BOOL hb_LZSSxCompressFile( FHANDLE hInput, FHANDLE hOutput, ULONG * pulSize )
|
|
{
|
|
PHB_LZSSX_COMPR pCompr;
|
|
ULONG ulSize;
|
|
|
|
pCompr = hb_LZSSxInit( hInput, NULL, 0, hOutput, NULL, 0 );
|
|
ulSize = hb_LZSSxEncode( pCompr );
|
|
hb_LZSSxExit( pCompr );
|
|
if( pulSize )
|
|
*pulSize = ulSize;
|
|
return ulSize != ( ULONG ) -1;
|
|
}
|
|
|
|
BOOL hb_LZSSxDecompressFile( FHANDLE hInput, FHANDLE hOutput )
|
|
{
|
|
PHB_LZSSX_COMPR pCompr;
|
|
BOOL fResult;
|
|
|
|
pCompr = hb_LZSSxInit( hInput, NULL, 0, hOutput, NULL, 0 );
|
|
fResult = hb_LZSSxDecode( pCompr );
|
|
hb_LZSSxExit( pCompr );
|
|
return fResult;
|
|
}
|
|
|
|
HB_FUNC( SX_FCOMPRESS )
|
|
{
|
|
BOOL fRet = FALSE;
|
|
FHANDLE hInput, hOutput;
|
|
char * szSource = hb_parc( 1 ), * szDestin = hb_parc( 2 );
|
|
BYTE buf[ 4 ];
|
|
ULONG ulSize;
|
|
|
|
if( szSource && *szSource && szDestin && *szDestin )
|
|
{
|
|
hInput = hb_fsExtOpen( ( BYTE * ) szSource, NULL, FO_READ | FO_DENYNONE |
|
|
FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL );
|
|
if( hInput != FS_ERROR )
|
|
{
|
|
hOutput = hb_fsExtOpen( ( BYTE * ) szDestin, NULL, FO_READWRITE |
|
|
FO_EXCLUSIVE | FXO_TRUNCATE |
|
|
FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL );
|
|
if( hOutput != FS_ERROR )
|
|
{
|
|
/* store uncompressed file size in first 4 bytes of destination
|
|
* file in little endian order - for SIX3 compatibility
|
|
*/
|
|
ulSize = hb_fsSeek( hInput, 0, FS_END );
|
|
if( hb_fsSeek( hInput, 0, FS_SET ) == 0 )
|
|
{
|
|
HB_PUT_LE_UINT32( buf, ulSize );
|
|
if( hb_fsWrite( hOutput, buf, 4 ) == 4 )
|
|
fRet = hb_LZSSxCompressFile( hInput, hOutput, NULL );
|
|
}
|
|
hb_fsClose( hOutput );
|
|
}
|
|
hb_fsClose( hInput );
|
|
}
|
|
}
|
|
hb_retl( fRet );
|
|
}
|
|
|
|
HB_FUNC( SX_FDECOMPRESS )
|
|
{
|
|
BOOL fRet = FALSE;
|
|
FHANDLE hInput, hOutput;
|
|
char * szSource = hb_parc( 1 ), * szDestin = hb_parc( 2 );
|
|
|
|
if( szSource && *szSource && szDestin && *szDestin )
|
|
{
|
|
hInput = hb_fsExtOpen( ( BYTE * ) szSource, NULL, FO_READ | FO_DENYNONE |
|
|
FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL );
|
|
if( hInput != FS_ERROR )
|
|
{
|
|
hOutput = hb_fsExtOpen( ( BYTE * ) szDestin, NULL, FO_READWRITE |
|
|
FO_EXCLUSIVE | FXO_TRUNCATE |
|
|
FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL );
|
|
if( hOutput != FS_ERROR )
|
|
{
|
|
/* skip the four bytes with original file length */
|
|
if( hb_fsSeek( hInput, 4, FS_SET ) == 4 )
|
|
fRet = hb_LZSSxDecompressFile( hInput, hOutput );
|
|
hb_fsClose( hOutput );
|
|
}
|
|
hb_fsClose( hInput );
|
|
}
|
|
}
|
|
hb_retl( fRet );
|
|
}
|
|
|
|
HB_FUNC( _SX_STRCOMPRESS )
|
|
{
|
|
BYTE * pStr = ( BYTE * ) hb_parc( 1 ), * pBuf;
|
|
|
|
if( pStr )
|
|
{
|
|
ULONG ulLen = hb_parclen( 1 ), ulBuf, ulDst;
|
|
|
|
/* this is for strict SIX compatibility - in general very bad idea */
|
|
ulBuf = ulLen + 257;
|
|
pBuf = ( BYTE * ) hb_xgrab( ulBuf );
|
|
HB_PUT_LE_UINT32( pBuf, ulLen );
|
|
if( ! hb_LZSSxCompressMem( pStr, ulLen, pBuf + 4, ulBuf - 4, &ulDst ) )
|
|
{
|
|
/* It's not six compatible - it's a workaround for wrongly defined SIX behavior */
|
|
HB_PUT_LE_UINT32( pBuf, HB_SX_UNCOMPRESED );
|
|
memcpy( pBuf + 4, pStr, ulLen );
|
|
}
|
|
hb_retclen( ( char * ) pBuf, ulDst + 4 );
|
|
hb_xfree( pBuf );
|
|
}
|
|
else
|
|
hb_itemReturn( hb_param( 1, HB_IT_ANY ) );
|
|
}
|
|
|
|
HB_FUNC( _SX_STRDECOMPRESS )
|
|
{
|
|
BOOL fOK = FALSE;
|
|
BYTE * pStr = ( BYTE * ) hb_parc( 1 ), * pBuf;
|
|
|
|
if( pStr )
|
|
{
|
|
ULONG ulLen = hb_parclen( 1 ), ulBuf;
|
|
|
|
if( ulLen >= 4 )
|
|
{
|
|
ulBuf = HB_GET_LE_UINT32( pStr );
|
|
if( ulBuf == HB_SX_UNCOMPRESED )
|
|
{
|
|
hb_retclen( ( char * ) pStr + 4, ulLen - 4 );
|
|
fOK = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pBuf = ( BYTE * ) hb_xalloc( ulBuf + 1 );
|
|
if( pBuf )
|
|
{
|
|
fOK = hb_LZSSxDecompressMem( pStr + 4, ulLen - 4, pBuf, ulBuf );
|
|
if( fOK )
|
|
hb_retclen_buffer( ( char * ) pBuf, ulBuf );
|
|
else
|
|
hb_xfree( pBuf );
|
|
}
|
|
else
|
|
{
|
|
PHB_ITEM pItem = hb_errRT_SubstParams( "SIXCOMPRESS", EG_MEM, 0, "possible compressed string corruption", "_SX_STRDECOMPRESS" );
|
|
if( pItem )
|
|
hb_itemRelease( hb_itemReturn( pItem ) );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !fOK )
|
|
hb_itemReturn( hb_param( 1, HB_IT_ANY ) );
|
|
}
|