296 lines
8.5 KiB
C++
296 lines
8.5 KiB
C++
/*
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
|
|
compliance with the License. You may obtain a copy of the License
|
|
at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
|
|
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
|
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
|
|
|
|
See the Apache Version 2.0 License for specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <gzipcompressionchanneltransform.h>
|
|
#include <wrappernativeinfo.h>
|
|
|
|
#pragma unmanaged
|
|
|
|
#ifdef LINKWITHZLIB
|
|
|
|
static const UInt32 s_defaultWriteSize = 256 * 1024;
|
|
|
|
GzipCompressionChannelTransform::GzipCompressionChannelTransform(DryadVertexProgram* vertex,
|
|
bool gzipHeader,
|
|
bool optimizeForSpeed)
|
|
{
|
|
// zlib structures
|
|
memset(&m_stream, 0, sizeof(m_stream));
|
|
m_stream.next_in = NULL;
|
|
m_stream.avail_in = 0;
|
|
m_stream.next_out = NULL;
|
|
m_stream.avail_out = 0;
|
|
m_stream.zalloc = Z_NULL;
|
|
m_stream.zfree = Z_NULL;
|
|
m_stream.opaque = NULL;
|
|
|
|
m_crc = crc32(0L, Z_NULL, 0);
|
|
m_crcStart = NULL;
|
|
m_channel = NULL;
|
|
m_writeSize = 0; /* try to infer this from the first item to process */
|
|
m_zlibArg = Z_NO_FLUSH;
|
|
m_firstReadProcessed = false;
|
|
m_gzipHeader = gzipHeader;
|
|
m_optimizeForSpeed = optimizeForSpeed;
|
|
m_vertex = vertex;
|
|
}
|
|
|
|
GzipCompressionChannelTransform::~GzipCompressionChannelTransform()
|
|
{
|
|
|
|
// zlib structures
|
|
memset(&m_stream, 0, sizeof(m_stream));
|
|
m_stream.next_in = NULL;
|
|
m_stream.avail_in = 0;
|
|
m_stream.next_out = NULL;
|
|
m_stream.avail_out = 0;
|
|
m_stream.zalloc = Z_NULL;
|
|
m_stream.zfree = Z_NULL;
|
|
m_stream.opaque = NULL;
|
|
|
|
m_crc = crc32(0L, Z_NULL, 0);
|
|
m_crcStart = NULL;
|
|
m_channel = NULL;
|
|
}
|
|
|
|
void GzipCompressionChannelTransform::AllocateOutputBuffer()
|
|
{
|
|
LogAssert(m_writeSize > 0);
|
|
m_outputBuffer.Attach(new DataBlockItem(m_writeSize));
|
|
m_stream.next_out = (byte *) m_outputBuffer->GetDataAddress();
|
|
LogAssert(m_outputBuffer.Ptr() != NULL);
|
|
m_stream.avail_out = (z_uInt) m_writeSize;
|
|
}
|
|
|
|
inline void GzipCompressionChannelTransform::AppendOutputByte(byte value)
|
|
{
|
|
//LogAssert(m_stream.avail_out > 0);
|
|
if (m_stream.avail_out == 0)
|
|
{
|
|
WriteOutputBuffer();
|
|
AllocateOutputBuffer();
|
|
}
|
|
m_stream.avail_out--;
|
|
*(m_stream.next_out) = value;
|
|
m_stream.next_out++;
|
|
}
|
|
|
|
inline void GzipCompressionChannelTransform::IncrementOutputPosition(UInt32 increment)
|
|
{
|
|
LogAssert(m_stream.avail_out >= increment);
|
|
m_stream.avail_out -= increment;
|
|
m_stream.next_out += increment;
|
|
}
|
|
|
|
void GzipCompressionChannelTransform::SetOutputBufferSize(UInt32 bufferSize)
|
|
{
|
|
WriteOutputBuffer();
|
|
m_writeSize = bufferSize;
|
|
AllocateOutputBuffer();
|
|
}
|
|
|
|
// Process block should be called with m_critsec held
|
|
DrError GzipCompressionChannelTransform::ProcessBlock()
|
|
{
|
|
int retVal = -1;
|
|
bool done = false;
|
|
while (!done)
|
|
{
|
|
// record the starting point for the crc
|
|
// DrLogI( "About to deflate",
|
|
// "avail_in %u total_in %u avail_out %u total_out %u arg %u",
|
|
// m_stream.avail_in, m_stream.total_in,
|
|
// m_stream.avail_out, m_stream.total_out, m_zlibArg);
|
|
m_crcStart = m_stream.next_in;
|
|
retVal = deflate(&m_stream, m_zlibArg);
|
|
// DrLogI( "Done deflate",
|
|
// "ret %u", retVal);
|
|
if (m_gzipHeader)
|
|
{
|
|
m_crc = crc32(m_crc, m_crcStart,
|
|
(UInt32) (m_stream.next_in - m_crcStart));
|
|
}
|
|
|
|
UInt32 *intPtr = NULL;
|
|
switch (retVal) {
|
|
case Z_OK:
|
|
// nothing needed here
|
|
break;
|
|
case Z_STREAM_END:
|
|
done = true;
|
|
if (m_gzipHeader)
|
|
{
|
|
if (m_stream.avail_out < 8)
|
|
{
|
|
WriteOutputBuffer();
|
|
AllocateOutputBuffer();
|
|
}
|
|
intPtr = (UInt32 *)m_stream.next_out;
|
|
*intPtr = m_crc;
|
|
intPtr++;
|
|
*intPtr = m_stream.total_in;
|
|
IncrementOutputPosition(8);
|
|
}
|
|
retVal = deflateEnd(&m_stream);
|
|
LogAssert(retVal == Z_OK);
|
|
WriteOutputBuffer();
|
|
AllocateOutputBuffer();
|
|
break;
|
|
default:
|
|
char *errorMsg = "NULL";
|
|
if (m_stream.msg != NULL) {
|
|
errorMsg = m_stream.msg;
|
|
}
|
|
DrLogA( "Error in deflate. retVal: %d Message: %s", retVal, errorMsg);
|
|
}
|
|
if (m_stream.avail_out == 0)
|
|
{
|
|
WriteOutputBuffer();
|
|
AllocateOutputBuffer();
|
|
}
|
|
if (m_stream.avail_in == 0 && m_zlibArg != Z_FINISH)
|
|
{
|
|
done = true;
|
|
}
|
|
}
|
|
return DrError_OK;
|
|
}
|
|
|
|
DrError GzipCompressionChannelTransform::ProcessItem(DataBlockItem *item)
|
|
{
|
|
UInt32 inputSize = (UInt32) item->GetAvailableSize();
|
|
if (inputSize == 0)
|
|
{
|
|
/* nothing to do here */
|
|
return DrError_OK;
|
|
}
|
|
|
|
{
|
|
AutoCriticalSection acs(&m_critsec);
|
|
|
|
if (m_firstReadProcessed == false)
|
|
{
|
|
if (m_writeSize == 0)
|
|
{
|
|
/* try to infer the write size from the size of the
|
|
uncompressed data coming in to the compresser,
|
|
i.e. what the app thought it wanted to write */
|
|
UInt32 itemSize = (UInt32) item->GetAllocatedSize();
|
|
UInt32 inputPages = itemSize / (4 * 1024);
|
|
if (inputPages * 4 * 1024 == itemSize)
|
|
{
|
|
m_writeSize = itemSize;
|
|
}
|
|
else
|
|
{
|
|
/* if the block is a weird size then use the
|
|
default so we don't force the writer to use
|
|
buffered IO */
|
|
m_writeSize = s_defaultWriteSize;
|
|
}
|
|
|
|
if (m_writeSize > 4 * 1024 * 1024)
|
|
{
|
|
m_writeSize = 4 * 1024 * 1024;
|
|
}
|
|
}
|
|
|
|
m_zlibArg = Z_NO_FLUSH;
|
|
|
|
AllocateOutputBuffer();
|
|
|
|
if (m_gzipHeader)
|
|
{
|
|
WriteGzipHeader();
|
|
}
|
|
|
|
int level = (m_optimizeForSpeed) ?
|
|
(Z_BEST_SPEED) : (Z_DEFAULT_COMPRESSION);
|
|
DrLogI( "Set compression level. Level %s", (m_optimizeForSpeed) ? "faster" : "default");
|
|
|
|
int retVal = deflateInit2(&m_stream, level, Z_DEFLATED,
|
|
-MAX_WBITS, 9, Z_DEFAULT_STRATEGY);
|
|
LogAssert(retVal == Z_OK);
|
|
|
|
m_firstReadProcessed = true;
|
|
}
|
|
|
|
LogAssert(m_zlibArg == Z_NO_FLUSH);
|
|
m_stream.avail_in = (z_uInt) item->GetAvailableSize();
|
|
m_stream.next_in = (z_Bytef *)item->GetDataAddress();
|
|
// DrLogI( "In process item");
|
|
return ProcessBlock();
|
|
}
|
|
}
|
|
|
|
DrError GzipCompressionChannelTransform::Finish(bool atEndOfStream)
|
|
{
|
|
{
|
|
DrError retval = DrError_OK;
|
|
AutoCriticalSection acs(&m_critsec);
|
|
|
|
if (m_firstReadProcessed)
|
|
{
|
|
m_zlibArg = Z_FINISH;
|
|
// DrLogI( "In finish");
|
|
retval = ProcessBlock();
|
|
|
|
m_firstReadProcessed = false;
|
|
}
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
DrError GzipCompressionChannelTransform::Start(FifoChannel *channel)
|
|
{
|
|
m_channel = channel;
|
|
return DrError_OK;
|
|
}
|
|
|
|
void GzipCompressionChannelTransform::WriteGzipHeader()
|
|
{
|
|
AppendOutputByte(0x1f); // ID1
|
|
AppendOutputByte(0x8b); // ID2
|
|
AppendOutputByte(8); // CM
|
|
for (int i = 0; i < 6; i++) {
|
|
AppendOutputByte(0); // FLAG, MTIME, and XFL
|
|
}
|
|
AppendOutputByte(11); // OS
|
|
}
|
|
|
|
|
|
void GzipCompressionChannelTransform::WriteOutputBuffer()
|
|
{
|
|
DataBlockItem *dbi = (DataBlockItem *)m_outputBuffer.Ptr();
|
|
int bytesToWrite = m_writeSize - m_stream.avail_out;
|
|
// DrLogI( "Writing output buffer",
|
|
// "bytes: %d", bytesToWrite);
|
|
if (bytesToWrite > 0) {
|
|
dbi->SetAvailableSize(bytesToWrite);
|
|
m_channel->WriteTransformedItem(dbi);
|
|
}
|
|
// for safety, zero the output pointers
|
|
m_stream.next_out = NULL;
|
|
m_stream.avail_out = 0;
|
|
m_outputBuffer = NULL;
|
|
}
|
|
#endif |