/* File: C:\WACKER\xfer\cmprs1.c (Created: 20-Jan-1994) * created from HAWIN source file * cmprs1.c -- Routines to implement data compression * * Copyright 1989,1994 by Hilgraeve Inc. -- Monroe, MI * All rights reserved * * $Revision: 1 $ * $Date: 10/05/98 1:16p $ */ #include #include #if !defined(BYTE) #define BYTE unsigned char #endif #include "cmprs.h" #include "cmprs.hh" #if SHOW // #include #endif unsigned int usPrefixCode = 0; /* code representing pattern matched so far */ int mcK; /* character to be appended to prefix for next match */ int (**ppfCmprsGetfunc)(void *) = NULL; /* pointer to the pointer to a function used by calling routine */ int (*pfCmprsGetChar)(void *); /* pointer to the function used internally to get data to compress */ void *pPsave; long *plCmprsLoadcnt; long lCmprsBegcnt; long lCmprsLimitcnt = 1L; // Initializing to one disables compression // shut-down unless changed struct s_cmprs_node *pstCmprsTbl; /* pointer to compression lookup table */ #define NODE_CAST struct s_cmprs_node * int lookup_code(void); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * compress_start * * DESCRIPTION * This function is called to begin data compression. The calling routine * should set up a pointer to a function through which it will make calls * to get characters of data. The pointer should be initialized to point * to the function that the compression routines should use to get raw * data for compression. The pointer is then modified by the compression * routines to point to the compressor. After compression is complete or * abandoned, the pointer is restored to its original value. * Example of calling sequence: * int (*xgetc)(); * int fgetc(); * * xgetc = fgetc; * if (compress_start(&xgetc)) * ; * If fPauses is TRUE, the compressor will flush existing data through when * the input function returns an EOF but will not shutdown. Whenever the * next non-EOF is retrieved, compression will resume where it left off will * the pattern table still intact. The fPauses flag must be used by both * the compression and decompression routines to work. If fPauses is used, * the cmprs_stop() function must be used to shut compression down before * compress_disable() is called. * * RETURN VALUE * Returns TRUE if memory is available for table storage and at least one * character is available from input; FALSE otherwise. */ int compress_start(int (**getfunc)(void *), void *pP, long *loadcnt, int fPauses) { #if FALSE #if !defined(LZTEST) long x; #endif #endif if (!compress_enable()) return(FALSE); fFlushable = fPauses; fxLastBuildGood = FALSE; /* By setting this FALSE, we will cause * compression to shut down if the very first * table build indicates that compression is * not effective. Thereafter, it will take two * consecutive bad builds to shut it down. */ if ((plCmprsLoadcnt = loadcnt) != NULL && !fFlushable) { lCmprsBegcnt = *plCmprsLoadcnt; /* * Compressability of files can be roughly measured by how many input * characters must be read before the pattern table fills up. The * lower the number, the less efficient compression is. This * calculation determines a cutoff point for any combination of * machine speed and transfer rate based on experimental trials. * * Note that this mechanism should not be used when the fPauses * parameter is TRUE because the decompressor would misinterpret * the data following the STOPCODE after compression shut down */ #if FALSE #if !defined(LZTEST) if ((x = (cnfg.bit_rate / cpu_speed())) == 0L) lCmprsLimitcnt = 4300L; else lCmprsLimitcnt = max(x * 774L - 500L, 4300L); #else lCmprsLimitcnt = 4300L; #endif #endif lCmprsLimitcnt = 4300L; } pPsave = pP; ppfCmprsGetfunc = getfunc; pfCmprsGetChar = *ppfCmprsGetfunc; if ((mcK = (*pfCmprsGetChar)(pPsave)) != EOF) { *ppfCmprsGetfunc = cmprs_getc; cmprs_inittbl(); ulHoldReg = 0L; ulHoldReg |= CLEARCODE; sBitsLeft = sCodeBits; usxCmprsStatus = COMPRESS_ACTIVE; #if SHOW printf("C %02X (starting, emit CLEARCODE)\n", mcK); printf("C -> %03X %08lX,%2d\n", CLEARCODE, ulHoldReg, sBitsLeft); #endif return(TRUE); } else { ppfCmprsGetfunc = NULL; return(FALSE); } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * compress_stop * * DESCRIPTION * If compression has been started, it is turned off. */ void compress_stop(void) { #if SHOW printf("C Compress_stop\n"); #endif if (ppfCmprsGetfunc != NULL) { *ppfCmprsGetfunc = pfCmprsGetChar; ppfCmprsGetfunc = NULL; } usxCmprsStatus = COMPRESS_IDLE; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * cmprs_inittbl * * DESCRIPTION * Used to initialize the lookup table used for compressing data. */ void cmprs_inittbl(void) { register INT iCount; sCodeBits = 9; usMaxCode = 512; usFreeCode = FIRSTFREE; // pstCmprsTbl = (struct s_cmprs_node *)(OFFSETOF(compress_tblspace)); pstCmprsTbl = (struct s_cmprs_node *)(compress_tblspace); for (iCount = 0; iCount < FIRSTFREE; ++iCount) pstCmprsTbl[iCount].first = pstCmprsTbl[iCount].next = NULL; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * cmprs_shutdown * * DESCRIPTION * This is the function that is installed by cmprs_getc when compression * is ending. It is installed after cmprs_getc encounters the end of the input * data. This function returns any remaining bytes, then returns EOF and * restores the original getc function * * RETURN VALUE * Returns the next code to be sent or EOF. */ int cmprs_shutdown(void *pX) { int mcRetCode; // If we haven't sent all the data yet, do so if (sBitsLeft > 0) { mcRetCode = (int)(ulHoldReg & 0x00FF); ulHoldReg >>= 8; sBitsLeft -= 8; #if SHOW printf("C %02X %08lX,%2d Draining ulHoldReg\n", mcRetCode, ulHoldReg, sBitsLeft); #endif } else { // No more data waiting. mcRetCode = EOF; sBitsLeft = 0; if (!fFlushable) { // Not flushable, get compression out of the chain *ppfCmprsGetfunc = pfCmprsGetChar; ppfCmprsGetfunc = NULL; #if SHOW printf(" !fFlushable, outta here\n"); #endif } else { // Flushable, see whether we should resume compression if ((mcK = (*pfCmprsGetChar)(pPsave)) != EOF) { #if SHOW printf("C %02X fFlushable TRUE, restarting\n", mcK); #endif *ppfCmprsGetfunc = cmprs_getc; mcRetCode = cmprs_getc(pPsave); } } } return(mcRetCode); } #if !USE_ASM /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * cmprs_getc * * DESCRIPTION * This is the function installed by compress_start to be used by any routine * that needs compressed data. It delivers bytes to the calling routine, * but may read several characters from the input to do so. * * RETURN VALUE * Returns next 8-bits of compressed data or EOF if no more is available. */ int cmprs_getc(void *pX) { int mcRetCode; int fBuildGood; if (sBitsLeft < 8) { usPrefixCode = (unsigned int)mcK; do { if ((mcK = (*pfCmprsGetChar)(pPsave)) == EOF) { /* at end of file, send last code followed by STOPCODE */ /* to stop decompression. Note that ulHoldReg may overflow */ /* if the maximum code size is greater than 12 bits */ ulHoldReg |= ((unsigned long)usPrefixCode << sBitsLeft); sBitsLeft += sCodeBits; #if SHOW printf("C -1 Shutdown," " emit prefix and STOPCODE\n"); printf("C -> %03X %08lX,%2d Codebits=%d\n", usPrefixCode, ulHoldReg, sBitsLeft, sCodeBits); #endif // If we're poised to switch to the next larger code size, // the decompressor will do so after the prior code, so // we should switch now too. if (usFreeCode >= usMaxCode && sCodeBits < MAXCODEBITS) { ++sCodeBits; usMaxCode *= 2; #if SHOW printf("C " "New sCodeBits = %d (anticipating)\n", sCodeBits); #endif } usPrefixCode = STOPCODE; *ppfCmprsGetfunc = cmprs_shutdown; usxCmprsStatus = COMPRESS_IDLE; break; /* let last code go out */ } } while (lookup_code()); ulHoldReg |= ((unsigned long)usPrefixCode << sBitsLeft); sBitsLeft += sCodeBits; #if SHOW printf("C -> %03X %08lX,%2d Codebits=%d\n", usPrefixCode, ulHoldReg, sBitsLeft, sCodeBits); #endif } mcRetCode = (int)(ulHoldReg & 0x00FF); ulHoldReg >>= 8; sBitsLeft -= 8; #if SHOW printf("C %02X %08lX,%2d\n", mcRetCode, ulHoldReg, sBitsLeft); #endif if (usFreeCode > usMaxCode) { /* We've used up all available codes at the current codesize */ if (sCodeBits >= MAXCODEBITS) { /* We've filled the pattern table, either shut down or clear the * table and build a new one. */ fBuildGood = TRUE; if (plCmprsLoadcnt && (*plCmprsLoadcnt - lCmprsBegcnt) < lCmprsLimitcnt) fBuildGood = FALSE; #if SHOW printf("C Table full, fBuildGood = %d\n", fBuildGood); #endif /* if two ineffective builds in a row (or if the very first build * is ineffective, shut compression down. */ if (!fBuildGood && !fxLastBuildGood) { /* compression is not effective, shut it down */ ulHoldReg |= ((unsigned long)STOPCODE << sBitsLeft); sBitsLeft += sCodeBits; #if SHOW printf("C -> %03X %08lX,%2d Ineffective, emitting STOPCODE\n", STOPCODE, ulHoldReg, sBitsLeft); #endif *ppfCmprsGetfunc = cmprs_shutdown; usxCmprsStatus = COMPRESS_SHUTDOWN; } else { /* clear the table and build a new one in case the nature of * the data changes. */ ulHoldReg |= ((unsigned long)CLEARCODE << sBitsLeft); sBitsLeft += sCodeBits; #if SHOW printf("C -> %03X %08lX,%2d New table, emiting CLEARCODE\n", CLEARCODE, ulHoldReg, sBitsLeft); #endif cmprs_inittbl(); lCmprsBegcnt = *plCmprsLoadcnt; } fxLastBuildGood = fBuildGood; } else { /* code size hasn't maxed out yet, bump to next larger code size */ ++sCodeBits; usMaxCode *= 2; #if SHOW printf("C New sCodeBits = %d, usMaxCode = %03X\n", sCodeBits, usMaxCode); #endif } } return(mcRetCode); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * lookup_code * * DESCRIPTION * This is a 'C' language version of the table lookup routine. It is used * when an internal lookup table is being used. An assembly language version * is used if an external lookup table is being used. * Given a current usPrefixCode and input character, this function * attempts to find a new usPrefixCode for the combined pattern in the table. * If so, it updates the usPrefixCode and returns TRUE. If the pattern is * not found, it adds the combination to the table and returns FALSE. * * RETURN VALUE * TRUE if usPrefixCode:mcK is found in the table. FALSE if not. */ int lookup_code(void) { int firstflag; struct s_cmprs_node *tptr = (NODE_CAST)&pstCmprsTbl[usPrefixCode]; struct s_cmprs_node *newptr; firstflag = TRUE; if (tptr->first != NULL) { firstflag = FALSE; tptr = tptr->first; for (;;) { if (tptr->cchar == (BYTE)mcK) { usPrefixCode = (unsigned int)(tptr - (NODE_CAST)(&pstCmprsTbl[0])); #if SHOW printf("C %02X ->(%03X)\n", mcK, usPrefixCode); #endif return(TRUE); } if (tptr->next == NULL) break; else tptr = tptr->next; } } if (usFreeCode < MAXNODES) { #if SHOW printf("C %02X Added %03X = %03X + %02X\n", mcK, usFreeCode, usPrefixCode, mcK); #endif newptr = (NODE_CAST)&pstCmprsTbl[usFreeCode++]; if (firstflag) tptr->first = newptr; else tptr->next = newptr; newptr->first = newptr->next = NULL; newptr->cchar = (BYTE)mcK; } else ++usFreeCode; /* triggers clearing and rebuilding of table */ return(FALSE); } #endif /* end of cmprs1.c */