mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
1488 lines
40 KiB
C++
1488 lines
40 KiB
C++
//
|
|
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
#include "opennurbs_zlib.h"
|
|
|
|
// RH-86025, RH3DM-179, 2025-02-14, Pierre:
|
|
// We only really need the #pragma comment(lib, "path") to work when we are using
|
|
// MSVC and the Microsoft linker, but not defining include dirs in build properties.
|
|
// Other compilers do not recognize this pragma and instead use build properties to link to libs.
|
|
// ON_CMAKE_BUILD should be renamed "ON_NOT_USING_MSVC_LINK_LIB_PRAGMA"
|
|
// This whole thing should be reworked so MSVC builds also use build properties,
|
|
// instead of this non-portable pragma.
|
|
#if defined(ON_COMPILER_MSC) && !defined(ON_CMAKE_BUILD)
|
|
|
|
#if !defined(OPENNURBS_ZLIB_LIB_DIR)
|
|
|
|
#include "opennurbs_input_libsdir.h"
|
|
|
|
#if defined(OPENNURBS_INPUT_LIBS_DIR)
|
|
// Typically, OPENNURBS_LIB_DIR is defined in opennurbs_msbuild.Cpp.props
|
|
#define OPENNURBS_ZLIB_LIB_DIR OPENNURBS_INPUT_LIBS_DIR
|
|
#else
|
|
// Define OPENNURBS_ZLIB_LIB_DIR to be the directory containing zlib.lib
|
|
#error You must define OPENNURBS_ZLIB_LIB_DIR
|
|
#endif // defined(OPENNURBS_INPUT_LIBS_DIR)
|
|
|
|
|
|
|
|
#if defined(_LIB) && defined(_MT) && !defined(_DLL)
|
|
// using Microsoft statically linked C-runtime
|
|
#pragma message ( "Linking with zlib_mt.lib in " OPENNURBS_PP2STR(OPENNURBS_ZLIB_LIB_DIR) )
|
|
#pragma comment(lib, "\"" OPENNURBS_ZLIB_LIB_DIR "/" "zlib_mt.lib" "\"")
|
|
#else
|
|
// using Microsoft DLL C-runtime
|
|
#pragma message ( "Linking with zlib.lib in " OPENNURBS_PP2STR(OPENNURBS_ZLIB_LIB_DIR) )
|
|
#pragma comment(lib, "\"" OPENNURBS_ZLIB_LIB_DIR "/" "zlib.lib" "\"")
|
|
#endif // defined(_LIB) && defined(_MT) && !defined(_DLL)
|
|
|
|
#endif // !defined(OPENNURBS_ZLIB_LIB_DIR)
|
|
|
|
#endif // defined(ON_COMPILER_MSC) && !defined(ON_CMAKE_BUILD)
|
|
|
|
// compressed buffer I/O uses zlib 1.1.3 inflate()/deflate()
|
|
class ON_CompressorImplementation
|
|
{
|
|
public:
|
|
ON_CompressorImplementation()
|
|
: m_mode(ON::archive_mode::unset_archive_mode)
|
|
{
|
|
ClearStream();
|
|
}
|
|
|
|
void ClearStream()
|
|
{
|
|
memset(&m_strm, 0, sizeof(z_stream));
|
|
}
|
|
|
|
ON::archive_mode m_mode; // ON::archive_mode::read = read and inflate, ON::archive_mode::write = deflate and write
|
|
enum
|
|
{
|
|
sizeof_x_buffer = 16384
|
|
};
|
|
unsigned char m_buffer[sizeof_x_buffer];
|
|
z_stream m_strm;
|
|
};
|
|
|
|
class ON_CompressorImplementation& ON_BinaryArchive::Compressor()
|
|
{
|
|
if (nullptr == m_compressor)
|
|
m_compressor = (class ON_CompressorImplementation*)oncalloc(1, sizeof(*m_compressor));
|
|
return *m_compressor;
|
|
}
|
|
|
|
|
|
bool ON_BinaryArchive::WriteCompressedBuffer(
|
|
size_t sizeof__inbuffer, // sizeof uncompressed input data
|
|
const void* inbuffer // uncompressed input data
|
|
)
|
|
{
|
|
size_t compressed_size = 0;
|
|
bool rc = false;
|
|
|
|
if ( !WriteMode() )
|
|
return false;
|
|
if ( sizeof__inbuffer > 0 && 0 == inbuffer )
|
|
return false;
|
|
if (sizeof__inbuffer > UINT32_MAX)
|
|
return false;
|
|
|
|
// number of bytes of uncompressed data
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
|
|
if (!WriteSize(sizeof__inbuffer))
|
|
return false;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
|
|
if ( 0 == sizeof__inbuffer )
|
|
return true;
|
|
|
|
// 32 bit crc of uncompressed data
|
|
const unsigned int buffer_crc = ON_CRC32( 0, sizeof__inbuffer, inbuffer );
|
|
if (!WriteInt(buffer_crc))
|
|
return false;
|
|
|
|
unsigned char method
|
|
= (m_bUseBufferCompression && sizeof__inbuffer > 128)
|
|
? 1
|
|
: 0;
|
|
|
|
if ( method ) {
|
|
if ( !CompressionInit() ) {
|
|
CompressionEnd();
|
|
method = 0;
|
|
}
|
|
}
|
|
if ( !WriteChar(method) )
|
|
return false;
|
|
|
|
switch ( method )
|
|
{
|
|
case 0: // uncompressed
|
|
rc = WriteByte(sizeof__inbuffer, inbuffer);
|
|
if ( rc )
|
|
{
|
|
compressed_size = sizeof__inbuffer;
|
|
}
|
|
break;
|
|
|
|
case 1: // compressed
|
|
compressed_size = WriteDeflate( sizeof__inbuffer, inbuffer );
|
|
rc = ( compressed_size > 0 ) ? true : false;
|
|
CompressionEnd();
|
|
break;
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BinaryArchive::ReadCompressedBufferSize( size_t* sizeof__outbuffer )
|
|
{
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
|
|
return ReadSize(sizeof__outbuffer);
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
}
|
|
|
|
bool ON_BinaryArchive::ReadCompressedBuffer( // read and uncompress
|
|
size_t sizeof__outbuffer, // sizeof of uncompressed buffer to read
|
|
void* outbuffer, // uncompressed output data returned here
|
|
bool* bFailedCRC
|
|
)
|
|
{
|
|
bool rc = false;
|
|
unsigned int buffer_crc0 = 0;
|
|
unsigned int buffer_crc1 = 0;
|
|
char method = 0;
|
|
|
|
if ( bFailedCRC)
|
|
*bFailedCRC = false;
|
|
if ( !ReadMode() )
|
|
return false;
|
|
if ( 0 == sizeof__outbuffer )
|
|
return true;
|
|
if ( 0 == outbuffer )
|
|
return false;
|
|
|
|
if ( !ReadInt(&buffer_crc0) ) // 32 bit crc of uncompressed buffer
|
|
return false;
|
|
|
|
if ( !ReadChar(&method) )
|
|
return false;
|
|
|
|
if ( method != 0 && method != 1 )
|
|
return false;
|
|
|
|
switch(method)
|
|
{
|
|
case 0: // uncompressed
|
|
rc = ReadByte(sizeof__outbuffer, outbuffer);
|
|
break;
|
|
case 1: // compressed
|
|
rc = CompressionInit();
|
|
if (rc)
|
|
rc = ReadInflate( sizeof__outbuffer, outbuffer );
|
|
CompressionEnd();
|
|
break;
|
|
}
|
|
|
|
if (rc )
|
|
{
|
|
buffer_crc1 = ON_CRC32( 0, sizeof__outbuffer, outbuffer );
|
|
if ( buffer_crc1 != buffer_crc0 )
|
|
{
|
|
ON_ERROR("ON_BinaryArchive::ReadCompressedBuffer() crc error");
|
|
if ( bFailedCRC )
|
|
*bFailedCRC = true;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
size_t ON_BinaryArchive::WriteDeflate( // returns number of bytes written
|
|
size_t sizeof___inbuffer, // sizeof uncompressed input data ( > 0 )
|
|
const void* in___buffer // uncompressed input data ( != nullptr )
|
|
)
|
|
{
|
|
/*
|
|
In "standard" (in 2005) 32 bit code
|
|
|
|
sizeof(int) = 4 bytes,
|
|
sizeof(long) = 4 bytes,
|
|
sizeof(pointer) = 4 bytes, and
|
|
sizeof(size_t) = 4 bytes.
|
|
|
|
Theoretically I don't need to use multiple input buffer
|
|
chunks in case. But I'm paranoid and I will use multiple
|
|
input chunks when sizeof_inbuffer > 2GB in order to dodge
|
|
any potential zlib signed verses unsigned compare bugs or
|
|
having a signed int i++ roll over to a negative number.
|
|
|
|
In "standard" code that has 64 bit pointers
|
|
|
|
sizeof(int) >= 4 bytes, (it's 4 on MS VS2005)
|
|
sizeof(long) >= 4 bytes, (it's 4 on MS VS2005)
|
|
sizeof(pointer) = 8 bytes, and
|
|
sizeof(size_t) = 8 bytes.
|
|
|
|
So, I'm going to assume the ints and longs in the zlib code
|
|
are 4 bytes, but I could have sizeof_inbuffer > 4GB.
|
|
This means I have to use multiple input buffer chunks.
|
|
In this case I still use multiple input chunks when
|
|
sizeof_inbuffer > 2GB in order to dodge any potential zlib
|
|
signed verses unsigned compare bugs or having a signed
|
|
int i++ roll over to a negative number.
|
|
|
|
So, I set
|
|
|
|
const size_t max_avail = (largest signed 4 byte integer - 15)
|
|
|
|
and feed inflate and deflate buffers with size <= max_avail.
|
|
|
|
|
|
This information below is from the zlib 1.2.3 FAQ.
|
|
|
|
32. Can zlib work with greater than 4 GB of data?
|
|
|
|
Yes. inflate() and deflate() will process any amount of data correctly.
|
|
Each call of inflate() or deflate() is limited to input and output chunks
|
|
of the maximum value that can be stored in the compiler's "unsigned int"
|
|
type, but there is no limit to the number of chunks. Note however that the
|
|
strm.total_in and strm_total_out counters may be limited to 4 GB. These
|
|
counters are provided as a convenience and are not used internally by
|
|
inflate() or deflate(). The application can easily set up its own counters
|
|
updated after each call of inflate() or deflate() to count beyond 4 GB.
|
|
compress() and uncompress() may be limited to 4 GB, since they operate in a
|
|
single call. gzseek() and gztell() may be limited to 4 GB depending on how
|
|
zlib is compiled. See the zlibCompileFlags() function in zlib.h.
|
|
|
|
The word "may" appears several times above since there is a 4 GB limit
|
|
only if the compiler's "long" type is 32 bits. If the compiler's "long"
|
|
type is 64 bits, then the limit is 16 exabytes.
|
|
*/
|
|
|
|
const size_t max_avail = 0x7FFFFFF0;
|
|
|
|
// Compressed information is saved in a chunk.
|
|
bool rc = BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,0);
|
|
if ( !rc )
|
|
return false;
|
|
|
|
class ON_CompressorImplementation& m_zlib(Compressor());
|
|
|
|
size_t out__count = 0;
|
|
int zrc = Z_OK;
|
|
|
|
size_t my_avail_in = sizeof___inbuffer;
|
|
unsigned char* my_next_in = (unsigned char*)in___buffer;
|
|
|
|
size_t d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
my_avail_in -= d;
|
|
my_next_in += d;
|
|
|
|
m_zlib.m_strm.next_out = m_zlib.m_buffer;
|
|
m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer;
|
|
|
|
// counter guards prevents infinite loops if there is a bug in zlib return codes.
|
|
int counter = 512;
|
|
int flush = Z_NO_FLUSH;
|
|
|
|
size_t deflate_output_count = 0;
|
|
|
|
while( rc && counter > 0 )
|
|
{
|
|
// Call zlib's deflate function. It can either process
|
|
// more input from m_zlib.m_strm.next_in[], create more
|
|
// compressed output in m_zlib.m_strm.next_out[], or do both.
|
|
if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in )
|
|
{
|
|
// no uncompressed input is left - switch to finish mode
|
|
flush = Z_FINISH;
|
|
}
|
|
zrc = z_deflate(&m_zlib.m_strm, flush);
|
|
if ( zrc < 0 )
|
|
{
|
|
// Something went haywire - bail out.
|
|
ON_ERROR("ON_BinaryArchive::WriteDeflate - z_deflate failure");
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
deflate_output_count = m_zlib.sizeof_x_buffer - m_zlib.m_strm.avail_out;
|
|
if ( deflate_output_count > 0 )
|
|
{
|
|
// The last call to deflate created output. Send
|
|
// this output to the archive.
|
|
rc = WriteChar( deflate_output_count, m_zlib.m_buffer );
|
|
if ( !rc )
|
|
break;
|
|
out__count += deflate_output_count;
|
|
m_zlib.m_strm.next_out = m_zlib.m_buffer;
|
|
m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer;
|
|
}
|
|
|
|
if ( Z_FINISH == flush && Z_STREAM_END == zrc )
|
|
{
|
|
// no input left, all pending compressing is finished,
|
|
// and all compressed output has been returned.
|
|
break;
|
|
}
|
|
|
|
if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail )
|
|
{
|
|
// inbuffer[] had more than max_zlib_avail_in bytes in it
|
|
// and I am feeding inbuffer[] to deflate in smaller chunks
|
|
// that the 32 bit integers in the zlib code can handle.
|
|
if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in )
|
|
{
|
|
// The call to deflate() used up all the input
|
|
// in m_zlib.m_strm.next_in[]. I can feed it another chunk
|
|
// from inbuffer[]
|
|
d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
}
|
|
else
|
|
{
|
|
// The call to deflate left some input in m_zlib.m_strm.next_in[],
|
|
// but I can increase m_zlib.m_strm.avail_in.
|
|
d = max_avail - m_zlib.m_strm.avail_in;
|
|
if ( d > my_avail_in )
|
|
d = my_avail_in;
|
|
m_zlib.m_strm.avail_in += (unsigned int)d;
|
|
}
|
|
|
|
my_avail_in -= d;
|
|
my_next_in += d;
|
|
}
|
|
else if ( 0 == deflate_output_count )
|
|
{
|
|
// no buffer changes this time
|
|
counter--;
|
|
}
|
|
|
|
if ( zrc != Z_OK )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !EndWrite3dmChunk() )
|
|
{
|
|
rc = false;
|
|
}
|
|
|
|
if ( 0 == counter )
|
|
{
|
|
rc = false;
|
|
}
|
|
|
|
return (rc ? out__count : 0);
|
|
}
|
|
|
|
|
|
bool ON_BinaryArchive::ReadInflate(
|
|
size_t sizeof___outbuffer, // sizeof uncompressed data
|
|
void* out___buffer // buffer for uncompressed data
|
|
)
|
|
{
|
|
const size_t max_avail = 0x7FFFFFF0; // See max_avail comment in ON_BinaryArchive::WriteInflate
|
|
|
|
size_t sizeof__inbuffer = 0;
|
|
void* in___buffer = 0;
|
|
bool rc = false;
|
|
|
|
// read compressed buffer from 3dm archive
|
|
bool bValidCompressedBuffer = false;
|
|
{
|
|
ON__UINT32 tcode = 0;
|
|
ON__INT64 big_value = 0;
|
|
rc = BeginRead3dmBigChunk(&tcode,&big_value );
|
|
if (!rc)
|
|
{
|
|
if ( 0 != out___buffer && sizeof___outbuffer > 0 )
|
|
memset(out___buffer,0,sizeof___outbuffer);
|
|
return false;
|
|
}
|
|
if ( tcode == TCODE_ANONYMOUS_CHUNK
|
|
&& big_value > 4
|
|
&& sizeof___outbuffer > 0
|
|
&& 0 != out___buffer )
|
|
{
|
|
// read compressed buffer from the archive
|
|
sizeof__inbuffer = (size_t)(big_value-4); // the last 4 bytes in this chunk are a 32 bit crc
|
|
in___buffer = onmalloc(sizeof__inbuffer);
|
|
if ( !in___buffer )
|
|
{
|
|
rc = false;
|
|
}
|
|
else
|
|
{
|
|
rc = ReadByte( sizeof__inbuffer, in___buffer );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Either I have the wrong chunk, or the input
|
|
// parameters are bogus.
|
|
rc = false;
|
|
}
|
|
unsigned int c0 = BadCRCCount();
|
|
if ( !EndRead3dmChunk() )
|
|
{
|
|
rc = false;
|
|
}
|
|
bValidCompressedBuffer = ( BadCRCCount() > c0 )
|
|
? false
|
|
: rc;
|
|
}
|
|
|
|
if ( !bValidCompressedBuffer && 0 != out___buffer && sizeof___outbuffer > 0 )
|
|
{
|
|
// Decompression will fail, but we might get something valid
|
|
// at the start if the data flaw was near the end of the buffer.
|
|
memset(out___buffer,0,sizeof___outbuffer);
|
|
}
|
|
|
|
if ( !rc )
|
|
{
|
|
if ( in___buffer )
|
|
{
|
|
onfree(in___buffer);
|
|
in___buffer = 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
class ON_CompressorImplementation& m_zlib(Compressor());
|
|
|
|
int zrc = -1;
|
|
|
|
// set up zlib in buffer
|
|
unsigned char* my_next_in = (unsigned char*)in___buffer;
|
|
size_t my_avail_in = sizeof__inbuffer;
|
|
|
|
size_t d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
my_next_in += d;
|
|
my_avail_in -= d;
|
|
|
|
// set up zlib out buffer
|
|
unsigned char* my_next_out = (unsigned char*)out___buffer;
|
|
size_t my_avail_out = sizeof___outbuffer;
|
|
|
|
d = my_avail_out;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_out = my_next_out;
|
|
m_zlib.m_strm.avail_out = (unsigned int)d;
|
|
my_next_out += d;
|
|
my_avail_out -= d;
|
|
|
|
// counter guards against infinite loop if there are
|
|
// bugs in zlib return codes
|
|
int counter = 512;
|
|
int flush = Z_NO_FLUSH;
|
|
|
|
while ( rc && counter > 0 )
|
|
{
|
|
// Call zlib's inflate function. It can either process
|
|
// more input from m_zlib.m_strm.next_in[], create more
|
|
// uncompressed output in m_zlib.m_strm.next_out[], or do both.
|
|
if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in )
|
|
{
|
|
// no compressed input is left - switch to finish mode
|
|
flush = Z_FINISH;
|
|
}
|
|
zrc = z_inflate( &m_zlib.m_strm, flush );
|
|
if ( zrc < 0 )
|
|
{
|
|
// Something went haywire - bail out.
|
|
ON_ERROR("ON_BinaryArchive::ReadInflate - z_inflate failure");
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
if ( Z_FINISH == flush && Z_STREAM_END == zrc )
|
|
{
|
|
// no input left, all pending decompression is finished,
|
|
// and all decompressed output has been returned.
|
|
break;
|
|
}
|
|
|
|
d = 0;
|
|
if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail )
|
|
{
|
|
if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in )
|
|
{
|
|
// The call to inflate() used up all the input
|
|
// in m_zlib.m_strm.next_in[]. I can feed it another chunk
|
|
// from inbuffer[]
|
|
d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
}
|
|
else
|
|
{
|
|
// The call to inflate() left some input in m_zlib.m_strm.next_in[],
|
|
// but I can increase m_zlib.m_strm.avail_in.
|
|
d = max_avail - m_zlib.m_strm.avail_in;
|
|
if ( d > my_avail_in )
|
|
d = my_avail_in;
|
|
m_zlib.m_strm.avail_in += (unsigned int)d;
|
|
}
|
|
my_next_in += d;
|
|
my_avail_in -= d;
|
|
}
|
|
|
|
if ( my_avail_out > 0 && m_zlib.m_strm.avail_out < max_avail )
|
|
{
|
|
// increase m_zlib.m_strm.next_out[] buffer
|
|
if ( 0 == m_zlib.m_strm.avail_out || 0 == m_zlib.m_strm.next_out )
|
|
{
|
|
d = my_avail_out;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_out = my_next_out;
|
|
m_zlib.m_strm.avail_out = (unsigned int)d;
|
|
}
|
|
else
|
|
{
|
|
d = max_avail - m_zlib.m_strm.avail_out;
|
|
if ( d > my_avail_out )
|
|
d = my_avail_out;
|
|
m_zlib.m_strm.avail_out += ((unsigned int)d);
|
|
}
|
|
my_next_out += d;
|
|
my_avail_out -= d;
|
|
}
|
|
else if ( 0 == d )
|
|
{
|
|
// no buffer changes
|
|
counter--;
|
|
}
|
|
}
|
|
|
|
if (in___buffer )
|
|
{
|
|
onfree(in___buffer);
|
|
in___buffer = 0;
|
|
}
|
|
|
|
if ( 0 == counter )
|
|
{
|
|
rc = false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BinaryArchive::CompressionInit()
|
|
{
|
|
// inflateInit() and deflateInit() are in zlib 1.3.3
|
|
bool rc = false;
|
|
if ( WriteMode() )
|
|
{
|
|
class ON_CompressorImplementation& m_zlib(Compressor());
|
|
|
|
rc = (m_zlib.m_mode == ON::archive_mode::write) ? true : false;
|
|
if ( !rc ) {
|
|
CompressionEnd();
|
|
if ( Z_OK == deflateInit( &m_zlib.m_strm, Z_BEST_COMPRESSION ) ) {
|
|
m_zlib.m_mode = ON::archive_mode::write;
|
|
rc = true;
|
|
}
|
|
else {
|
|
memset(&m_zlib.m_strm,0,sizeof(m_zlib.m_strm));
|
|
}
|
|
}
|
|
}
|
|
else if ( ReadMode() )
|
|
{
|
|
class ON_CompressorImplementation& m_zlib(Compressor());
|
|
|
|
rc = (m_zlib.m_mode == ON::archive_mode::read) ? true : false;
|
|
if ( !rc ) {
|
|
CompressionEnd();
|
|
if ( Z_OK == inflateInit( &m_zlib.m_strm ) ) {
|
|
m_zlib.m_mode = ON::archive_mode::read;
|
|
rc = true;
|
|
}
|
|
else {
|
|
memset(&m_zlib.m_strm,0,sizeof(m_zlib.m_strm));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
CompressionEnd();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void ON_BinaryArchive::CompressionEnd()
|
|
{
|
|
// inflateEnd() and deflateEnd() are in zlib 1.3.3
|
|
if (0 != m_compressor)
|
|
{
|
|
switch (m_compressor->m_mode)
|
|
{
|
|
case ON::archive_mode::read:
|
|
case ON::archive_mode::read3dm:
|
|
inflateEnd(&m_compressor->m_strm);
|
|
break;
|
|
case ON::archive_mode::write:
|
|
case ON::archive_mode::write3dm:
|
|
deflateEnd(&m_compressor->m_strm);
|
|
break;
|
|
default: // to quiet lint
|
|
break;
|
|
}
|
|
m_compressor->ClearStream();
|
|
m_compressor->m_mode = ON::archive_mode::unset_archive_mode;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
struct ON_CompressedBufferHelper
|
|
{
|
|
int m_action; // 1 = compress, 2 = uncompress
|
|
enum
|
|
{
|
|
sizeof_x_buffer = 16384
|
|
};
|
|
unsigned char m_buffer[sizeof_x_buffer];
|
|
z_stream m_strm;
|
|
size_t m_buffer_compressed_capacity;
|
|
};
|
|
|
|
ON_CompressedBuffer::ON_CompressedBuffer()
|
|
: m_sizeof_uncompressed(0),
|
|
m_sizeof_compressed(0),
|
|
m_crc_uncompressed(0),
|
|
m_crc_compressed(0),
|
|
m_method(0),
|
|
m_sizeof_element(0),
|
|
m_buffer_compressed_capacity(0),
|
|
m_buffer_compressed(0)
|
|
{
|
|
}
|
|
|
|
ON_CompressedBuffer::~ON_CompressedBuffer()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
ON_CompressedBuffer::ON_CompressedBuffer(const ON_CompressedBuffer& src)
|
|
: m_sizeof_uncompressed(0),
|
|
m_sizeof_compressed(0),
|
|
m_crc_uncompressed(0),
|
|
m_crc_compressed(0),
|
|
m_method(0),
|
|
m_sizeof_element(0),
|
|
m_buffer_compressed_capacity(0),
|
|
m_buffer_compressed(0)
|
|
{
|
|
*this = src;
|
|
}
|
|
|
|
ON_CompressedBuffer& ON_CompressedBuffer::operator=(const ON_CompressedBuffer& src)
|
|
{
|
|
if ( this != &src )
|
|
{
|
|
Destroy();
|
|
if( src.m_buffer_compressed && src.m_sizeof_compressed > 0 )
|
|
{
|
|
m_sizeof_uncompressed = src.m_sizeof_uncompressed;
|
|
m_sizeof_compressed = src.m_sizeof_compressed;
|
|
m_crc_uncompressed = src.m_crc_uncompressed;
|
|
m_crc_compressed = src.m_crc_compressed;
|
|
m_method = src.m_method;
|
|
m_sizeof_element = src.m_sizeof_element;
|
|
|
|
m_buffer_compressed = onmalloc(m_sizeof_compressed);
|
|
if( m_buffer_compressed )
|
|
{
|
|
m_buffer_compressed_capacity = m_sizeof_compressed;
|
|
memcpy(m_buffer_compressed,src.m_buffer_compressed,m_sizeof_compressed);
|
|
}
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::Write( ON_BinaryArchive& binary_archive ) const
|
|
{
|
|
bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
|
|
if ( !rc )
|
|
return false;
|
|
|
|
for(;;)
|
|
{
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
|
|
rc = binary_archive.WriteSize(m_sizeof_uncompressed);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.WriteSize((m_buffer_compressed && m_sizeof_compressed>0) ? m_sizeof_compressed : 0);
|
|
if (!rc)
|
|
break;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
rc = binary_archive.WriteInt(m_crc_uncompressed);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.WriteInt(m_crc_compressed);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.WriteInt(m_method);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.WriteInt(m_sizeof_element);
|
|
if (!rc)
|
|
break;
|
|
if ( m_buffer_compressed && m_sizeof_compressed > 0 )
|
|
{
|
|
rc = binary_archive.WriteByte(m_sizeof_compressed,m_buffer_compressed);
|
|
if (!rc)
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( !binary_archive.EndWrite3dmChunk() )
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::Read( ON_BinaryArchive& binary_archive )
|
|
{
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
|
|
if ( !rc )
|
|
return false;
|
|
|
|
for(;;)
|
|
{
|
|
rc = ( 1 == major_version );
|
|
if ( !rc )
|
|
break;
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
|
|
rc = binary_archive.ReadSize(&m_sizeof_uncompressed);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.ReadSize(&m_sizeof_compressed);
|
|
if (!rc)
|
|
break;
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
rc = binary_archive.ReadInt(&m_crc_uncompressed);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.ReadInt(&m_crc_compressed);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.ReadInt(&m_method);
|
|
if (!rc)
|
|
break;
|
|
rc = binary_archive.ReadInt(&m_sizeof_element);
|
|
if (!rc)
|
|
break;
|
|
if ( m_sizeof_compressed > 0 )
|
|
{
|
|
m_buffer_compressed = onmalloc(m_sizeof_compressed);
|
|
if ( m_buffer_compressed )
|
|
{
|
|
m_buffer_compressed_capacity = m_sizeof_compressed;
|
|
rc = binary_archive.ReadByte(m_sizeof_compressed,m_buffer_compressed);
|
|
}
|
|
else
|
|
{
|
|
m_sizeof_compressed =0;
|
|
}
|
|
if (!rc)
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( !binary_archive.EndRead3dmChunk() )
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
void ON_CompressedBuffer::Destroy()
|
|
{
|
|
if ( m_buffer_compressed )
|
|
onfree(m_buffer_compressed);
|
|
|
|
m_sizeof_uncompressed = 0;
|
|
m_sizeof_compressed = 0;
|
|
m_crc_uncompressed = 0;
|
|
m_crc_compressed = 0;
|
|
m_method = 0;
|
|
m_sizeof_element = 0;
|
|
m_buffer_compressed = 0;
|
|
m_buffer_compressed_capacity = 0;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::Compress(
|
|
size_t sizeof__inbuffer, // sizeof uncompressed input data
|
|
const void* inbuffer, // uncompressed input data
|
|
int sizeof_element
|
|
)
|
|
{
|
|
Destroy();
|
|
|
|
//size_t compressed_size = 0;
|
|
bool rc = false;
|
|
|
|
if ( sizeof__inbuffer > 0 && 0 == inbuffer )
|
|
return false;
|
|
|
|
if ( 0 == sizeof__inbuffer )
|
|
return true;
|
|
|
|
if (sizeof__inbuffer > UINT32_MAX)
|
|
return false;
|
|
|
|
// number of bytes of uncompressed data
|
|
m_sizeof_uncompressed = sizeof__inbuffer;
|
|
|
|
ON_CompressedBufferHelper helper;
|
|
memset(&helper,0,sizeof(helper));
|
|
helper.m_action = 1;
|
|
|
|
bool bToggleByteOrder = false;
|
|
switch(sizeof_element)
|
|
{
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
if ( 0 == (sizeof__inbuffer%sizeof_element) )
|
|
{
|
|
m_sizeof_element = sizeof_element;
|
|
bToggleByteOrder = (ON::endian::big_endian == ON::Endian());
|
|
}
|
|
break;
|
|
};
|
|
|
|
if ( bToggleByteOrder )
|
|
{
|
|
ON_BinaryFile::ToggleByteOrder(
|
|
(int)(sizeof__inbuffer/m_sizeof_element),
|
|
m_sizeof_element,
|
|
inbuffer,
|
|
(void*)inbuffer
|
|
);
|
|
}
|
|
|
|
m_method = (sizeof__inbuffer > 128) ? 1 : 0;
|
|
if ( m_method )
|
|
{
|
|
if ( !CompressionInit(&helper) )
|
|
{
|
|
CompressionEnd(&helper);
|
|
m_method = 0;
|
|
}
|
|
else
|
|
{
|
|
m_buffer_compressed = onmalloc(sizeof__inbuffer/4);
|
|
size_t sizeof_compressed = DeflateHelper( &helper, sizeof__inbuffer, inbuffer );
|
|
CompressionEnd(&helper);
|
|
if ( sizeof_compressed > 0 && sizeof_compressed == m_sizeof_compressed )
|
|
{
|
|
rc = true;
|
|
if ( 2*m_buffer_compressed_capacity > 3*m_sizeof_compressed )
|
|
{
|
|
// release memory we don't need
|
|
m_buffer_compressed_capacity = m_sizeof_compressed;
|
|
m_buffer_compressed = onrealloc(m_buffer_compressed,m_buffer_compressed_capacity);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Destroy();
|
|
m_method = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0 == m_method )
|
|
{
|
|
// uncompressed
|
|
m_buffer_compressed = onmalloc(sizeof__inbuffer);
|
|
if ( m_buffer_compressed )
|
|
{
|
|
m_sizeof_compressed = sizeof__inbuffer;
|
|
m_buffer_compressed_capacity = sizeof__inbuffer;
|
|
memcpy(m_buffer_compressed,inbuffer,sizeof__inbuffer);
|
|
rc = true;
|
|
}
|
|
}
|
|
|
|
if ( bToggleByteOrder )
|
|
{
|
|
ON_BinaryFile::ToggleByteOrder(
|
|
(int)(sizeof__inbuffer/m_sizeof_element),
|
|
m_sizeof_element,
|
|
inbuffer,
|
|
(void*)inbuffer
|
|
);
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
m_crc_uncompressed = ON_CRC32( 0, sizeof__inbuffer, inbuffer );
|
|
m_crc_compressed = ON_CRC32( 0, m_sizeof_compressed, m_buffer_compressed );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::Uncompress(
|
|
void* outbuffer,
|
|
int* bFailedCRC
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
|
|
if ( bFailedCRC)
|
|
*bFailedCRC = false;
|
|
if ( 0 == m_sizeof_uncompressed )
|
|
return true;
|
|
if ( 0 == outbuffer )
|
|
return false;
|
|
|
|
if ( m_method != 0 && m_method != 1 )
|
|
return false;
|
|
|
|
ON__UINT32 compressed_crc = ON_CRC32( 0, m_sizeof_compressed, m_buffer_compressed );
|
|
if ( compressed_crc != m_crc_compressed )
|
|
{
|
|
// m_buffer_compressed is corrupt - let's hope the corruption
|
|
// is near the end and we ge something useful from the
|
|
// beginning.
|
|
memset(outbuffer,0,m_sizeof_uncompressed);
|
|
if ( bFailedCRC)
|
|
*bFailedCRC = false;
|
|
}
|
|
|
|
switch(m_method)
|
|
{
|
|
case 0: // uncompressed
|
|
if ( m_buffer_compressed
|
|
&& m_sizeof_uncompressed == m_sizeof_compressed
|
|
)
|
|
{
|
|
memcpy(outbuffer,m_buffer_compressed,m_sizeof_uncompressed);
|
|
rc = true;
|
|
}
|
|
break;
|
|
|
|
case 1: // compressed
|
|
{
|
|
ON_CompressedBufferHelper helper;
|
|
memset(&helper,0,sizeof(helper));
|
|
helper.m_action = 2;
|
|
rc = CompressionInit(&helper);
|
|
if (rc)
|
|
{
|
|
rc = InflateHelper( &helper, m_sizeof_uncompressed, outbuffer );
|
|
CompressionEnd(&helper);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch(m_sizeof_element)
|
|
{
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
if ( 0 == (m_sizeof_uncompressed%m_sizeof_element) )
|
|
{
|
|
if (ON::endian::big_endian == ON::Endian())
|
|
{
|
|
ON_BinaryFile::ToggleByteOrder(
|
|
(int)(m_sizeof_uncompressed/m_sizeof_element),
|
|
m_sizeof_element,
|
|
outbuffer,
|
|
outbuffer
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
};
|
|
|
|
|
|
if (rc )
|
|
{
|
|
ON__UINT32 uncompressed_crc = ON_CRC32( 0, m_sizeof_uncompressed, outbuffer );
|
|
if ( uncompressed_crc != m_crc_uncompressed )
|
|
{
|
|
ON_ERROR("ON_CompressedBuffer::Uncompress() crc error");
|
|
if ( bFailedCRC )
|
|
*bFailedCRC = true;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::WriteChar(
|
|
size_t count, const void* buffer
|
|
)
|
|
{
|
|
bool rc = true;
|
|
if ( count > 0 && buffer )
|
|
{
|
|
if ( count + m_sizeof_compressed > m_buffer_compressed_capacity )
|
|
{
|
|
size_t delta = count + m_sizeof_compressed - m_buffer_compressed_capacity;
|
|
if ( delta < 2048 )
|
|
delta = 2048;
|
|
if ( delta < m_buffer_compressed_capacity/4 )
|
|
delta = m_buffer_compressed_capacity/4;
|
|
m_buffer_compressed_capacity += delta;
|
|
m_buffer_compressed = onrealloc(m_buffer_compressed,m_buffer_compressed_capacity);
|
|
if ( !m_buffer_compressed )
|
|
{
|
|
m_buffer_compressed_capacity = 0;
|
|
m_sizeof_compressed = 0;
|
|
return false;
|
|
}
|
|
}
|
|
memcpy(((char*)m_buffer_compressed)+m_sizeof_compressed,buffer,count);
|
|
m_sizeof_compressed += count;
|
|
}
|
|
else
|
|
{
|
|
rc = (0 == count);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
size_t ON_CompressedBuffer::DeflateHelper( // returns number of bytes written
|
|
ON_CompressedBufferHelper* helper,
|
|
size_t sizeof___inbuffer, // sizeof uncompressed input data ( > 0 )
|
|
const void* in___buffer // uncompressed input data ( != nullptr )
|
|
)
|
|
{
|
|
/*
|
|
In "standard" (in 2005) 32 bit code
|
|
|
|
sizeof(int) = 4 bytes,
|
|
sizeof(long) = 4 bytes,
|
|
sizeof(pointer) = 4 bytes, and
|
|
sizeof(size_t) = 4 bytes.
|
|
|
|
Theoretically I don't need to use multiple input buffer
|
|
chunks in case. But I'm paranoid and I will use multiple
|
|
input chunks when sizeof_inbuffer > 2GB in order to dodge
|
|
any potential zlib signed verses unsigned compare bugs or
|
|
having a signed int i++ roll over to a negative number.
|
|
|
|
In "standard" code that has 64 bit pointers
|
|
|
|
sizeof(int) >= 4 bytes, (it's 4 on MS VS2005)
|
|
sizeof(long) >= 4 bytes, (it's 4 on MS VS2005)
|
|
sizeof(pointer) = 8 bytes, and
|
|
sizeof(size_t) = 8 bytes.
|
|
|
|
So, I'm going to assume the ints and longs in the zlib code
|
|
are 4 bytes, but I could have sizeof_inbuffer > 4GB.
|
|
This means I have to use multiple input buffer chunks.
|
|
In this case I still use multiple input chunks when
|
|
sizeof_inbuffer > 2GB in order to dodge any potential zlib
|
|
signed verses unsigned compare bugs or having a signed
|
|
int i++ roll over to a negative number.
|
|
|
|
So, I set
|
|
|
|
const size_t max_avail = (largest signed 4 byte integer - 15)
|
|
|
|
and feed inflate and deflate buffers with size <= max_avail.
|
|
|
|
|
|
This information below is from the zlib 1.2.3 FAQ.
|
|
|
|
32. Can zlib work with greater than 4 GB of data?
|
|
|
|
Yes. inflate() and deflate() will process any amount of data correctly.
|
|
Each call of inflate() or deflate() is limited to input and output chunks
|
|
of the maximum value that can be stored in the compiler's "unsigned int"
|
|
type, but there is no limit to the number of chunks. Note however that the
|
|
strm.total_in and strm_total_out counters may be limited to 4 GB. These
|
|
counters are provided as a convenience and are not used internally by
|
|
inflate() or deflate(). The application can easily set up its own counters
|
|
updated after each call of inflate() or deflate() to count beyond 4 GB.
|
|
compress() and uncompress() may be limited to 4 GB, since they operate in a
|
|
single call. gzseek() and gztell() may be limited to 4 GB depending on how
|
|
zlib is compiled. See the zlibCompileFlags() function in zlib.h.
|
|
|
|
The word "may" appears several times above since there is a 4 GB limit
|
|
only if the compiler's "long" type is 32 bits. If the compiler's "long"
|
|
type is 64 bits, then the limit is 16 exabytes.
|
|
*/
|
|
|
|
const size_t max_avail = 0x7FFFFFF0;
|
|
|
|
// Compressed information is saved in a chunk.
|
|
bool rc = true;
|
|
|
|
size_t out__count = 0;
|
|
int zrc = Z_OK;
|
|
|
|
size_t my_avail_in = sizeof___inbuffer;
|
|
unsigned char* my_next_in = (unsigned char*)in___buffer;
|
|
|
|
size_t d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
|
|
ON_CompressedBufferHelper& m_zlib = *helper;
|
|
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
my_avail_in -= d;
|
|
my_next_in += d;
|
|
|
|
m_zlib.m_strm.next_out = m_zlib.m_buffer;
|
|
m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer;
|
|
|
|
// counter guards prevents infinite loops if there is a bug in zlib return codes.
|
|
int counter = 512;
|
|
int flush = Z_NO_FLUSH;
|
|
|
|
size_t deflate_output_count = 0;
|
|
|
|
while( rc && counter > 0 )
|
|
{
|
|
// Call zlib's deflate function. It can either process
|
|
// more input from m_zlib.m_strm.next_in[], create more
|
|
// compressed output in m_zlib.m_strm.next_out[], or do both.
|
|
if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in )
|
|
{
|
|
// no uncompressed input is left - switch to finish mode
|
|
flush = Z_FINISH;
|
|
}
|
|
zrc = z_deflate( &m_zlib.m_strm, flush );
|
|
if ( zrc < 0 )
|
|
{
|
|
// Something went haywire - bail out.
|
|
ON_ERROR("ON_CompressedBuffer::DeflateHelper - z_deflate failure");
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
deflate_output_count = m_zlib.sizeof_x_buffer - m_zlib.m_strm.avail_out;
|
|
if ( deflate_output_count > 0 )
|
|
{
|
|
// The last call to deflate created output. Send
|
|
// this output to the archive.
|
|
rc = WriteChar( deflate_output_count, m_zlib.m_buffer );
|
|
if ( !rc )
|
|
break;
|
|
out__count += deflate_output_count;
|
|
m_zlib.m_strm.next_out = m_zlib.m_buffer;
|
|
m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer;
|
|
}
|
|
|
|
if ( Z_FINISH == flush && Z_STREAM_END == zrc )
|
|
{
|
|
// no input left, all pending compressing is finished,
|
|
// and all compressed output has been returned.
|
|
break;
|
|
}
|
|
|
|
if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail )
|
|
{
|
|
// inbuffer[] had more than max_zlib_avail_in bytes in it
|
|
// and I am feeding inbuffer[] to deflate in smaller chunks
|
|
// that the 32 bit integers in the zlib code can handle.
|
|
if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in )
|
|
{
|
|
// The call to deflate() used up all the input
|
|
// in m_zlib.m_strm.next_in[]. I can feed it another chunk
|
|
// from inbuffer[]
|
|
d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
}
|
|
else
|
|
{
|
|
// The call to deflate left some input in m_zlib.m_strm.next_in[],
|
|
// but I can increase m_zlib.m_strm.avail_in.
|
|
d = max_avail - m_zlib.m_strm.avail_in;
|
|
if ( d > my_avail_in )
|
|
d = my_avail_in;
|
|
m_zlib.m_strm.avail_in += (unsigned int)d;
|
|
}
|
|
|
|
my_avail_in -= d;
|
|
my_next_in += d;
|
|
}
|
|
else if ( 0 == deflate_output_count )
|
|
{
|
|
// no buffer changes this time
|
|
counter--;
|
|
}
|
|
|
|
if ( zrc != Z_OK )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( 0 == counter )
|
|
{
|
|
rc = false;
|
|
}
|
|
|
|
return (rc ? out__count : 0);
|
|
}
|
|
|
|
|
|
bool ON_CompressedBuffer::InflateHelper(
|
|
ON_CompressedBufferHelper* helper,
|
|
size_t sizeof___outbuffer, // sizeof uncompressed data
|
|
void* out___buffer // buffer for uncompressed data
|
|
) const
|
|
{
|
|
const size_t max_avail = 0x7FFFFFF0; // See max_avail comment in ON_CompressedBuffer::InflateHelper
|
|
|
|
bool rc = true;
|
|
|
|
int zrc = -1;
|
|
|
|
// set up zlib in buffer
|
|
unsigned char* my_next_in = (unsigned char*)m_buffer_compressed;
|
|
size_t my_avail_in = m_sizeof_compressed;
|
|
|
|
size_t d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
|
|
struct ON_CompressedBufferHelper& m_zlib = *helper;
|
|
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
my_next_in += d;
|
|
my_avail_in -= d;
|
|
|
|
// set up zlib out buffer
|
|
unsigned char* my_next_out = (unsigned char*)out___buffer;
|
|
size_t my_avail_out = sizeof___outbuffer;
|
|
|
|
d = my_avail_out;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_out = my_next_out;
|
|
m_zlib.m_strm.avail_out = (unsigned int)d;
|
|
my_next_out += d;
|
|
my_avail_out -= d;
|
|
|
|
// counter guards against infinite loop if there are
|
|
// bugs in zlib return codes
|
|
int counter = 512;
|
|
int flush = Z_NO_FLUSH;
|
|
|
|
while ( rc && counter > 0 )
|
|
{
|
|
// Call zlib's inflate function. It can either process
|
|
// more input from m_zlib.m_strm.next_in[], create more
|
|
// uncompressed output in m_zlib.m_strm.next_out[], or do both.
|
|
if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in )
|
|
{
|
|
// no compressed input is left - switch to finish mode
|
|
flush = Z_FINISH;
|
|
}
|
|
zrc = z_inflate( &m_zlib.m_strm, flush );
|
|
if ( zrc < 0 )
|
|
{
|
|
// Something went haywire - bail out.
|
|
ON_ERROR("ON_CompressedBuffer::InflateHelper - z_inflate failure");
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
if ( Z_FINISH == flush && Z_STREAM_END == zrc )
|
|
{
|
|
// no input left, all pending decompression is finished,
|
|
// and all decompressed output has been returned.
|
|
break;
|
|
}
|
|
|
|
d = 0;
|
|
if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail )
|
|
{
|
|
if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in )
|
|
{
|
|
// The call to inflate() used up all the input
|
|
// in m_zlib.m_strm.next_in[]. I can feed it another chunk
|
|
// from inbuffer[]
|
|
d = my_avail_in;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_in = my_next_in;
|
|
m_zlib.m_strm.avail_in = (unsigned int)d;
|
|
}
|
|
else
|
|
{
|
|
// The call to inflate() left some input in m_zlib.m_strm.next_in[],
|
|
// but I can increase m_zlib.m_strm.avail_in.
|
|
d = max_avail - m_zlib.m_strm.avail_in;
|
|
if ( d > my_avail_in )
|
|
d = my_avail_in;
|
|
m_zlib.m_strm.avail_in += (unsigned int)d;
|
|
}
|
|
my_next_in += d;
|
|
my_avail_in -= d;
|
|
}
|
|
|
|
if ( my_avail_out > 0 && m_zlib.m_strm.avail_out < max_avail )
|
|
{
|
|
// increase m_zlib.m_strm.next_out[] buffer
|
|
if ( 0 == m_zlib.m_strm.avail_out || 0 == m_zlib.m_strm.next_out )
|
|
{
|
|
d = my_avail_out;
|
|
if ( d > max_avail )
|
|
d = max_avail;
|
|
m_zlib.m_strm.next_out = my_next_out;
|
|
m_zlib.m_strm.avail_out = (unsigned int)d;
|
|
}
|
|
else
|
|
{
|
|
d = max_avail - m_zlib.m_strm.avail_out;
|
|
if ( d > my_avail_out )
|
|
d = my_avail_out;
|
|
m_zlib.m_strm.avail_out += ((unsigned int)d);
|
|
}
|
|
my_next_out += d;
|
|
my_avail_out -= d;
|
|
}
|
|
else if ( 0 == d )
|
|
{
|
|
// no buffer changes
|
|
counter--;
|
|
}
|
|
}
|
|
|
|
if ( 0 == counter )
|
|
{
|
|
rc = false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::CompressionInit( struct ON_CompressedBufferHelper* helper ) const
|
|
{
|
|
bool rc = false;
|
|
|
|
if ( helper )
|
|
{
|
|
// inflateInit() and deflateInit() are in zlib 1.3.3
|
|
if ( 1 == helper->m_action )
|
|
{
|
|
// begin compression using zlib's deflate tool
|
|
if (Z_OK == deflateInit(&helper->m_strm, Z_BEST_COMPRESSION))
|
|
{
|
|
rc = true;
|
|
}
|
|
else
|
|
{
|
|
memset(&helper->m_strm, 0, sizeof(helper->m_strm));
|
|
helper->m_action = 0;
|
|
}
|
|
}
|
|
else if (2 == helper->m_action)
|
|
{
|
|
// begin uncompression using zlib's inflate tool
|
|
if (Z_OK == inflateInit(&helper->m_strm))
|
|
{
|
|
rc = true;
|
|
}
|
|
else
|
|
{
|
|
memset(&helper->m_strm, 0, sizeof(helper->m_strm));
|
|
helper->m_action = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_CompressedBuffer::CompressionEnd( struct ON_CompressedBufferHelper* helper ) const
|
|
{
|
|
bool rc = false;
|
|
|
|
if ( helper )
|
|
{
|
|
// inflateEnd() and deflateEnd() are in zlib 1.3.3
|
|
if (1 == helper->m_action)
|
|
{
|
|
// finish compression
|
|
deflateEnd(&helper->m_strm);
|
|
rc = true;
|
|
}
|
|
else if (2 == helper->m_action)
|
|
{
|
|
// finish decompression
|
|
inflateEnd(&helper->m_strm);
|
|
rc = true;
|
|
}
|
|
memset(&helper->m_strm, 0, sizeof(helper->m_strm));
|
|
helper->m_action = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|