Add wxLZMAOutputStream for compressing data using LZMA
As liblzma API is similar to zlib API, this class is also close to wxZlibOutputStream, except that it uses reusable functions instead of repeating their code.
This commit is contained in:
@@ -704,6 +704,7 @@ Related overview: @ref overview_stream
|
|||||||
@li wxStringInputStream: String input stream class
|
@li wxStringInputStream: String input stream class
|
||||||
@li wxStringOutputStream: String output stream class
|
@li wxStringOutputStream: String output stream class
|
||||||
@li wxLZMAInputStream: LZMA decompression stream class
|
@li wxLZMAInputStream: LZMA decompression stream class
|
||||||
|
@li wxLZMAOutputStream: LZMA compression stream class
|
||||||
@li wxZlibInputStream: Zlib and gzip (compression) input stream class
|
@li wxZlibInputStream: Zlib and gzip (compression) input stream class
|
||||||
@li wxZlibOutputStream: Zlib and gzip (compression) output stream class
|
@li wxZlibOutputStream: Zlib and gzip (compression) output stream class
|
||||||
@li wxZipInputStream: Input stream for reading from ZIP archives
|
@li wxZipInputStream: Input stream for reading from ZIP archives
|
||||||
|
@@ -71,6 +71,52 @@ private:
|
|||||||
void Init();
|
void Init();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Filter for compressing data using LZMA(2) algorithm
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class WXDLLIMPEXP_BASE wxLZMAOutputStream : public wxFilterOutputStream,
|
||||||
|
private wxPrivate::wxLZMAData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit wxLZMAOutputStream(wxOutputStream& stream, int level = -1)
|
||||||
|
: wxFilterOutputStream(stream)
|
||||||
|
{
|
||||||
|
Init(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit wxLZMAOutputStream(wxOutputStream* stream, int level = -1)
|
||||||
|
: wxFilterOutputStream(stream)
|
||||||
|
{
|
||||||
|
Init(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~wxLZMAOutputStream() { Close(); }
|
||||||
|
|
||||||
|
void Sync() wxOVERRIDE { DoFlush(false); }
|
||||||
|
bool Close() wxOVERRIDE;
|
||||||
|
wxFileOffset GetLength() const wxOVERRIDE { return m_pos; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t OnSysWrite(const void *buffer, size_t size) wxOVERRIDE;
|
||||||
|
wxFileOffset OnSysTell() const wxOVERRIDE { return m_pos; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Init(int level);
|
||||||
|
|
||||||
|
// Write the contents of the internal buffer to the output stream.
|
||||||
|
bool UpdateOutput();
|
||||||
|
|
||||||
|
// Write out the current buffer if necessary, i.e. if no space remains in
|
||||||
|
// it, and reinitialize m_stream to point to it. Returns false on success
|
||||||
|
// or false on error, in which case m_lasterror is updated.
|
||||||
|
bool UpdateOutputIfNecessary();
|
||||||
|
|
||||||
|
// Run LZMA_FINISH (if argument is true) or LZMA_FULL_FLUSH, return true on
|
||||||
|
// success or false on error.
|
||||||
|
bool DoFlush(bool finish);
|
||||||
|
};
|
||||||
|
|
||||||
WXDLLIMPEXP_BASE wxVersionInfo wxGetLibLZMAVersionInfo();
|
WXDLLIMPEXP_BASE wxVersionInfo wxGetLibLZMAVersionInfo();
|
||||||
|
|
||||||
#endif // wxUSE_LIBLZMA && wxUSE_STREAMS
|
#endif // wxUSE_LIBLZMA && wxUSE_STREAMS
|
||||||
|
@@ -33,7 +33,7 @@
|
|||||||
@library{wxbase}
|
@library{wxbase}
|
||||||
@category{archive,streams}
|
@category{archive,streams}
|
||||||
|
|
||||||
@see wxInputStream, wxZlibInputStream
|
@see wxInputStream, wxZlibInputStream, wxLZMAOutputStream.
|
||||||
|
|
||||||
@since 3.1.2
|
@since 3.1.2
|
||||||
*/
|
*/
|
||||||
@@ -58,3 +58,41 @@ public:
|
|||||||
*/
|
*/
|
||||||
wxLZMAInputStream(wxInputStream* stream);
|
wxLZMAInputStream(wxInputStream* stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
@class wxLZMAOutputStream
|
||||||
|
|
||||||
|
This filter stream compresses data using XZ format.
|
||||||
|
|
||||||
|
XZ format uses LZMA compression, making it (significantly) more efficient
|
||||||
|
than Gzip format used by wxZlibOutputStream. Output generated by this class
|
||||||
|
is compatible with xz utilities working with .xz files and also supported
|
||||||
|
by 7-Zip program, even though it is different from its native .7z format.
|
||||||
|
|
||||||
|
@library{wxbase}
|
||||||
|
@category{archive,streams}
|
||||||
|
|
||||||
|
@see wxOutputStream, wxZlibOutputStream, wxLZMAInputStream
|
||||||
|
|
||||||
|
@since 3.1.2
|
||||||
|
*/
|
||||||
|
class wxLZMAOutputStream : public wxFilterOutputStream
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
Create compressing stream associated with the given underlying
|
||||||
|
stream.
|
||||||
|
|
||||||
|
This overload does not take ownership of the @a stream.
|
||||||
|
*/
|
||||||
|
wxLZMAOutputStream(wxOutputStream& stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create compressing stream associated with the given underlying
|
||||||
|
stream and takes ownership of it.
|
||||||
|
|
||||||
|
As with the base wxFilterOutputStream class, passing @a stream by
|
||||||
|
pointer indicates that this object takes ownership of it and will
|
||||||
|
delete it when it is itself destroyed.
|
||||||
|
*/
|
||||||
|
wxLZMAOutputStream(wxOutputStream* stream);
|
||||||
|
};
|
||||||
|
@@ -209,4 +209,164 @@ size_t wxLZMAInputStream::OnSysRead(void* outbuf, size_t size)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// wxLZMAOutputStream: compression
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void wxLZMAOutputStream::Init(int level)
|
||||||
|
{
|
||||||
|
if ( level == -1 )
|
||||||
|
level = LZMA_PRESET_DEFAULT;
|
||||||
|
|
||||||
|
// Use the check type recommended by liblzma documentation.
|
||||||
|
const lzma_ret rc = lzma_easy_encoder(m_stream, level, LZMA_CHECK_CRC64);
|
||||||
|
switch ( rc )
|
||||||
|
{
|
||||||
|
case LZMA_OK:
|
||||||
|
// Prepare for the first call to OnSysWrite().
|
||||||
|
m_stream->next_out = m_streamBuf;
|
||||||
|
m_stream->avail_out = wxLZMA_BUF_SIZE;
|
||||||
|
|
||||||
|
// Skip setting m_lasterror below.
|
||||||
|
return;
|
||||||
|
|
||||||
|
case LZMA_MEM_ERROR:
|
||||||
|
wxLogError(_("Failed to allocate memory for LZMA compression."));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
wxLogError(_("Failed to initialize LZMA compression: "
|
||||||
|
"unexpected error %u."),
|
||||||
|
rc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t wxLZMAOutputStream::OnSysWrite(const void *inbuf, size_t size)
|
||||||
|
{
|
||||||
|
m_stream->next_in = static_cast<const uint8_t*>(inbuf);
|
||||||
|
m_stream->avail_in = size;
|
||||||
|
|
||||||
|
// Compress as long as we have any input data, but stop at first error as
|
||||||
|
// it's useless to try to continue after it (or even starting if the stream
|
||||||
|
// had already been in an error state).
|
||||||
|
while ( m_lasterror == wxSTREAM_NO_ERROR && m_stream->avail_in > 0 )
|
||||||
|
{
|
||||||
|
// Flush the output buffer if necessary.
|
||||||
|
if ( !UpdateOutputIfNecessary() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const lzma_ret rc = lzma_code(m_stream, LZMA_RUN);
|
||||||
|
|
||||||
|
wxString err;
|
||||||
|
switch ( rc )
|
||||||
|
{
|
||||||
|
case LZMA_OK:
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case LZMA_MEM_ERROR:
|
||||||
|
err = wxTRANSLATE("out of memory");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LZMA_STREAM_END:
|
||||||
|
// This is unexpected as we don't use LZMA_FINISH here.
|
||||||
|
wxFAIL_MSG( "Unexpected LZMA stream end" );
|
||||||
|
wxFALLTHROUGH;
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = wxTRANSLATE("unknown compression error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxLogError(_("LZMA compression error: %s"), wxGetTranslation(err));
|
||||||
|
|
||||||
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pos += size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxLZMAOutputStream::UpdateOutput()
|
||||||
|
{
|
||||||
|
// Write the buffer contents to the real output, taking care only to write
|
||||||
|
// as much of it as we actually have, as the buffer can (and very likely
|
||||||
|
// will) be incomplete.
|
||||||
|
const size_t numOut = wxLZMA_BUF_SIZE - m_stream->avail_out;
|
||||||
|
m_parent_o_stream->Write(m_streamBuf, numOut);
|
||||||
|
if ( m_parent_o_stream->LastWrite() != numOut )
|
||||||
|
{
|
||||||
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxLZMAOutputStream::UpdateOutputIfNecessary()
|
||||||
|
{
|
||||||
|
if ( !m_stream->avail_out )
|
||||||
|
{
|
||||||
|
if ( !UpdateOutput() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_stream->next_out = m_streamBuf;
|
||||||
|
m_stream->avail_out = wxLZMA_BUF_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxLZMAOutputStream::DoFlush(bool finish)
|
||||||
|
{
|
||||||
|
const lzma_action action = finish ? LZMA_FINISH : LZMA_FULL_FLUSH;
|
||||||
|
|
||||||
|
while ( m_lasterror == wxSTREAM_NO_ERROR )
|
||||||
|
{
|
||||||
|
if ( !UpdateOutputIfNecessary() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
const lzma_ret rc = lzma_code(m_stream, action);
|
||||||
|
|
||||||
|
wxString err;
|
||||||
|
switch ( rc )
|
||||||
|
{
|
||||||
|
case LZMA_OK:
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case LZMA_STREAM_END:
|
||||||
|
// Don't forget to output the last part of the data.
|
||||||
|
return UpdateOutput();
|
||||||
|
|
||||||
|
case LZMA_MEM_ERROR:
|
||||||
|
err = wxTRANSLATE("out of memory");
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = wxTRANSLATE("unknown compression error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxLogError(_("LZMA compression error when flushing output: %s"),
|
||||||
|
wxGetTranslation(err));
|
||||||
|
|
||||||
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxLZMAOutputStream::Close()
|
||||||
|
{
|
||||||
|
if ( !DoFlush(true) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_stream->next_out = m_streamBuf;
|
||||||
|
m_stream->avail_out = wxLZMA_BUF_SIZE;
|
||||||
|
|
||||||
|
return wxFilterOutputStream::Close() && IsOk();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // wxUSE_LIBLZMA && wxUSE_STREAMS
|
#endif // wxUSE_LIBLZMA && wxUSE_STREAMS
|
||||||
|
Reference in New Issue
Block a user