gzip.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • zlib/
  • gzip.cc
  • View
  • Commits
  • Open Download .zip Download (7 KB)
/*
 * Copyright 1995, 2003 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

# include <stdhdrs.h>

# include <debug.h>
# include <strbuf.h>
# include <error.h>

# include "zlib.h"
# include "zutil.h"
# include "gzip.h"
# include <msgsupp.h>

/*
 * gz_magic -- the gzip magic header, in a simple form.
 */

static const char gz_magic[] = {
	0x1f, 0x8b, 
	Z_DEFLATED, 
	0,			/*flags*/
	0,0,0,0,		/*time*/
	0,			/*xflags*/
	0x03 			/*OS_CODE*/
} ;

#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
#define COMMENT      0x10 /* bit 4 set: file comment present */
#define RESERVED     0xE0 /* bits 5..7: reserved */

Gzip::Gzip()
{
	// this is used for both inflate & deflate

	zstream = new z_stream;
	zstream->zalloc = (alloc_func)0;
	zstream->zfree = (free_func)0;
	zstream->opaque = (voidpf)0;
	isInflate = 0;
	isDeflate = 0;
	ws = we = 0;
	state = 0;
}

Gzip::~Gzip()
{
	if( isInflate ) inflateEnd( zstream );
	if( isDeflate ) deflateEnd( zstream );
	delete zstream;
}

enum GzipState { 

	GZ_INIT,

	// Compress() states

	GZC_COMPRESS,
	GZC_FLUSH,
	GZC_FINAL,
	GZC_DONE,

	// Uncompress() states

	GZU_MAGIC,
	GZU_HEADERS,
	GZU_HEADER_EXTRA,
	GZU_HEADER_EXTRA_READ,
	GZU_HEADER_STRING,
	GZU_UNCOMPRESS,
	GZU_DONE

};

int
Gzip::Compress( Error *e )
{
	int err;

	for(;;)
	{
	    /* Handle any buffer copying */

	    if( ws < we )
	    {
		// copy ws->we to os->oe

		int l = we-ws < oe-os ? we-ws : oe-os;
		memcpy( os, ws, l );
		os += l, ws += l;

		// Output full -- return to caller for more room.

		if( os == oe )
		    return 1;
	    }

	    switch( state )
	    {
	    case GZ_INIT:

		// Initialize compressor

		isDeflate = 1;
		crc = crc32(0L, Z_NULL, 0);

		if( deflateInit2(
			zstream,
			Z_DEFAULT_COMPRESSION,
			Z_DEFLATED,
			-MAX_WBITS,		// - to suppress zlib header!
			DEF_MEM_LEVEL, 0 ) 
			!= Z_OK )
		{
		    e->Set( MsgSupp::DeflateInit );
		    return 0;
		}

		// Arrange to copy out gzip header

		ws = (char *)gz_magic;
		we = ws + sizeof( gz_magic );

		state = GZC_COMPRESS;
		continue;

	    case GZC_COMPRESS:

		// Finished?

		if( !is )
		{
		    state = GZC_FLUSH;
		    continue;
		}

		// Now just wrapping deflate()

		zstream->next_in = (Bytef *)is;
		zstream->avail_in = ie - is;
		zstream->next_out = (Bytef *)os;
		zstream->avail_out = oe - os;

		if( deflate( zstream, Z_NO_FLUSH ) != Z_OK )
		{
		    e->Set( MsgSupp::Deflate );
		    return 0;
		}

		// catch up on CRC

		crc = crc32(crc, (Bytef *)is, zstream->next_in - (Bytef *)is );

		is = (char *)zstream->next_in;
		os = (char *)zstream->next_out;
		
		return 1;

	    case GZC_FLUSH:

		// flush deflater
		// even deflate(Z_FINISH) requires defined _in vars

		zstream->next_in = 0;
		zstream->avail_in = 0;
		zstream->next_out = (Bytef *)os;
		zstream->avail_out = oe - os;

		err = deflate( zstream, Z_FINISH );

		os = (char *)zstream->next_out;

		if( err == Z_OK )
		    return 1;

		if( err != Z_STREAM_END )
		{
		    e->Set( MsgSupp::Deflate );
		    return 0;
		}

		if( deflateEnd( zstream ) != Z_OK )
		{
		    e->Set( MsgSupp::DeflateEnd );
		    return 0;
		}

		state = GZC_FINAL;
		continue;

	    case GZC_FINAL:

		// Write crc & and length

		tmpbuf[0] = ( crc >> 0 ) & 0xff;
		tmpbuf[1] = ( crc >> 8 ) & 0xff;
		tmpbuf[2] = ( crc >> 16 ) & 0xff;
		tmpbuf[3] = ( crc >> 24 ) & 0xff;

		tmpbuf[4] = ( zstream->total_in >> 0 ) & 0xff;
		tmpbuf[5] = ( zstream->total_in >> 8 ) & 0xff;
		tmpbuf[6] = ( zstream->total_in >> 16 ) & 0xff;
		tmpbuf[7] = ( zstream->total_in >> 24 ) & 0xff;

		ws = tmpbuf;
		we = ws + 8;
		
		state = GZC_DONE;
		continue;

	    case GZC_DONE:

		return 0;
	    }
	}
}

int
Gzip::Uncompress( Error *e )
{
	int err;
	char *p;

	for(;;)
	{
	    /* Handle any buffer copying */

	    if( ws < we )
	    {
		// copy is->ie to ws->we

		int l = we-ws < ie-is ? we-ws : ie-is;
		memcpy( ws, is, l );
		is += l, ws += l;

		// input empty -- return to caller for more
		// This protects GZU_UNCOMPRESS which assumes EOF
		// on no input.

		if( is == ie )
		    return 1;
	    }

	    switch( state )
	    {
	    case GZ_INIT:

		// Initialize decompressor

		isInflate = 1;
		crc = crc32(0L, Z_NULL, 0);

		if( inflateInit2( zstream, -DEF_WBITS ) != Z_OK )
		{
		    e->Set( MsgSupp::InflateInit );
		    return 0;
		}

		// Fill magic header

		ws = tmpbuf;
		we = tmpbuf + sizeof( gz_magic );

		state = GZU_MAGIC;
		continue;

	    case GZU_MAGIC:

		// And the magic header

		if( memcmp( tmpbuf, gz_magic, 3 ) )
		{
		    e->Set( MsgSupp::MagicHeader );
		    return 0;
		}

		// Just in case this file was written by gzip and not by us.

		hflags = tmpbuf[3];
		state = GZU_HEADERS;
		continue;

	    case GZU_HEADERS:

		/* What headers are left to process? */
		/* Switch them off from flags as done. */

		if( hflags & EXTRA_FIELD )
		{
		    // 2 byte length + length bytes
		    hflags &= ~EXTRA_FIELD;
		    ws = tmpbuf;
		    we = tmpbuf + 2;
		    state = GZU_HEADER_EXTRA;
		    continue;
		}
		else if( hflags & ORIG_NAME )
		{
		    // null terminated string
		    hflags &= ~ORIG_NAME;
		    state = GZU_HEADER_STRING;
		    continue;
		}
		else if( hflags & COMMENT )
		{
		    // null terminated string
		    hflags &= ~COMMENT;
		    state = GZU_HEADER_STRING;
		    continue;
		}
		else if( hflags & HEAD_CRC )
		{
		    // 2 bytes
		    // back to GZU_HEADERS when done
		    hflags &= ~HEAD_CRC;
		    ws = tmpbuf;
		    we = tmpbuf + 2;
		    continue;
		}

		state = GZU_UNCOMPRESS;
		continue;

	    case GZU_HEADER_EXTRA:

		// Taken length and read that many bytes.

		hxlen = 
		    ((unsigned int)tmpbuf[0]) << 0 | 
		    ((unsigned int)tmpbuf[1]) << 8;

		state = GZU_HEADER_EXTRA_READ;
		continue;

	    case GZU_HEADER_EXTRA_READ:

		// Read hxlen bytes, then return to GZU_HEADERS

		if( ie - is < hxlen )
		{
		    hxlen -= ie - is;
		    is = ie;
		    return 1;
		}

		is += hxlen;
		state = GZU_HEADERS;
		continue;

	    case GZU_HEADER_STRING:

		// Look for a null, then return to GZU_HEADERS

		if( !( p = (char *)memchr( is, 0, ie - is ) ) )
		{
		    is = ie;
		    return 1;
		}

		is = p + 1;
		state = GZU_HEADERS;
		continue;

	    case GZU_UNCOMPRESS:

		// now just wrapping inflate()

		zstream->next_in = (Bytef *)is;
		zstream->avail_in = ie - is;
		zstream->next_out = (Bytef *)os;
		zstream->avail_out = oe - os;

	    	err = inflate( zstream, Z_NO_FLUSH );

		// catch up on CRC

		crc = crc32(crc, (Bytef *)os, zstream->next_out - (Bytef *)os );

		is = (char *)zstream->next_in;
		os = (char *)zstream->next_out;

		if( err == Z_OK )
		    return 1;

		if( err != Z_STREAM_END )
		{
		    e->Set( MsgSupp::Inflate );
		    return 0;
		}

		state = GZU_DONE;
		continue;

	    case GZU_DONE:

		return 0;
	    }
	}
}

# Change User Description Committed
#2 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code