kdecore: new KCompressor and KDecompressor classes replacing KFilterDev

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-10-17 16:36:39 +03:00
parent efd9978abf
commit 860b2d098d
28 changed files with 776 additions and 2251 deletions

View file

@ -55,12 +55,12 @@ if(ENABLE_TESTING)
endif() endif()
# required features # required features
find_package(ZLIB) find_package(LibDeflate)
set_package_properties(ZLIB PROPERTIES set_package_properties(ZLIB PROPERTIES
DESCRIPTION "Support for gzip compressed files and data streams" DESCRIPTION "Heavily optimized library for DEFLATE/zlib/gzip compression and decompression"
URL "http://www.zlib.net" URL "https://github.com/ebiggers/libdeflate"
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Required by the core KDE libraries and some critical kioslaves" PURPOSE "Compression and decompression of DEFLATE/zlib/gzip format"
) )
find_package(SharedMimeInfo 0.91) find_package(SharedMimeInfo 0.91)

View file

@ -0,0 +1,31 @@
# Try to find libdeflate, once done this will define:
#
# LIBDEFLATE_FOUND - system has libdeflate
# LIBDEFLATE_INCLUDES - the libdeflate include directory
# LIBDEFLATE_LIBRARIES - the libraries needed to use libdeflate
#
# Copyright (c) 2022 Ivailo Monev <xakepa10@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# libdeflate does not provide pkg-config files
find_package(PkgConfig REQUIRED)
include(FindPackageHandleStandardArgs)
find_path(LIBDEFLATE_INCLUDES
NAMES libdeflate.h
HINTS $ENV{LIBDEFLATEDIR}/include
)
find_library(LIBDEFLATE_LIBRARIES
NAMES deflate
HINTS $ENV{LIBDEFLATEDIR}/lib
)
find_package_handle_standard_args(LibDeflate
REQUIRED_VARS LIBDEFLATE_LIBRARIES LIBDEFLATE_INCLUDES
)
mark_as_advanced(LIBDEFLATE_INCLUDES LIBDEFLATE_LIBRARIES)

View file

@ -132,8 +132,8 @@ install(
KFileMetaInfoItem KFileMetaInfoItem
KFilePlacesModel KFilePlacesModel
KFileTreeView KFileTreeView
KFilterBase KCompressor
KFilterDev KDecompressor
KFind KFind
KFindDialog KFindDialog
KFloatValidator KFloatValidator

1
includes/KCompressor Normal file
View file

@ -0,0 +1 @@
#include "../kcompressor.h"

1
includes/KDecompressor Normal file
View file

@ -0,0 +1 @@
#include "../kdecompressor.h"

View file

@ -1 +0,0 @@
#include "../kfilterbase.h"

View file

@ -1 +0,0 @@
#include "../kfilterdev.h"

View file

@ -31,7 +31,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}/compression)
include_directories( include_directories(
${KDE4_KDECORE_INCLUDES} ${KDE4_KDECORE_INCLUDES}
${KDE4_KDEUI_INCLUDES} ${KDE4_KDEUI_INCLUDES}
${ZLIB_INCLUDE_DIR} ${LIBDEFLATE_INCLUDES}
${QT_INCLUDES} ${QT_INCLUDES}
${CMAKE_CURRENT_SOURCE_DIR}/auth ${CMAKE_CURRENT_SOURCE_DIR}/auth
${CMAKE_CURRENT_SOURCE_DIR}/sonnet ${CMAKE_CURRENT_SOURCE_DIR}/sonnet
@ -49,14 +49,12 @@ add_definitions(-DQT_NO_CAST_FROM_ASCII)
# compile bzip2 support if available # compile bzip2 support if available
if(BZIP2_FOUND) if(BZIP2_FOUND)
include_directories(${BZIP2_INCLUDE_DIR}) include_directories(${BZIP2_INCLUDE_DIR})
set(kdecore_OPTIONAL_SRCS ${kdecore_OPTIONAL_SRCS} compression/kbzip2filter.cpp)
set(kdecore_OPTIONAL_LIBS ${kdecore_OPTIONAL_LIBS} ${BZIP2_LIBRARIES}) set(kdecore_OPTIONAL_LIBS ${kdecore_OPTIONAL_LIBS} ${BZIP2_LIBRARIES})
endif(BZIP2_FOUND) endif(BZIP2_FOUND)
# compile lzma support if available # compile lzma support if available
if(LIBLZMA_FOUND) if(LIBLZMA_FOUND)
include_directories(${LIBLZMA_INCLUDE_DIRS}) include_directories(${LIBLZMA_INCLUDE_DIRS})
set(kdecore_OPTIONAL_SRCS ${kdecore_OPTIONAL_SRCS} compression/kxzfilter.cpp)
set(kdecore_OPTIONAL_LIBS ${kdecore_OPTIONAL_LIBS} ${LIBLZMA_LIBRARIES}) set(kdecore_OPTIONAL_LIBS ${kdecore_OPTIONAL_LIBS} ${LIBLZMA_LIBRARIES})
endif(LIBLZMA_FOUND) endif(LIBLZMA_FOUND)
@ -89,9 +87,8 @@ endif()
########### next target ############### ########### next target ###############
set(kdecore_LIB_SRCS set(kdecore_LIB_SRCS
compression/kgzipfilter.cpp compression/kcompressor.cpp
compression/kfilterbase.cpp compression/kdecompressor.cpp
compression/kfilterdev.cpp
config/kconfig.cpp config/kconfig.cpp
config/kconfigbase.cpp config/kconfigbase.cpp
config/kconfigdata.cpp config/kconfigdata.cpp
@ -219,7 +216,7 @@ endif()
add_library(kdecore ${LIBRARY_TYPE} ${kdecore_LIB_SRCS}) add_library(kdecore ${LIBRARY_TYPE} ${kdecore_LIB_SRCS})
target_link_libraries(kdecore PRIVATE target_link_libraries(kdecore PRIVATE
${ZLIB_LIBRARY} ${LIBDEFLATE_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${kdecore_OPTIONAL_LIBS} ${kdecore_OPTIONAL_LIBS}
) )
@ -277,8 +274,8 @@ install(
install( install(
FILES FILES
${CMAKE_CURRENT_BINARY_DIR}/kdecore_export.h ${CMAKE_CURRENT_BINARY_DIR}/kdecore_export.h
compression/kfilterbase.h compression/kcompressor.h
compression/kfilterdev.h compression/kdecompressor.h
config/kconfig.h config/kconfig.h
config/kconfigbase.h config/kconfigbase.h
config/kconfiggroup.h config/kconfiggroup.h

View file

@ -1,6 +1,2 @@
kde4_bool_to_01(BZIP2_FOUND HAVE_BZIP2_SUPPORT) kde4_bool_to_01(BZIP2_FOUND HAVE_BZIP2_SUPPORT)
if(BZIP2_FOUND AND BZIP2_NEED_PREFIX)
set(NEED_BZ2_PREFIX 1)
endif()
kde4_bool_to_01(LIBLZMA_FOUND HAVE_XZ_SUPPORT) kde4_bool_to_01(LIBLZMA_FOUND HAVE_XZ_SUPPORT)

View file

@ -1,8 +1,5 @@
#cmakedefine01 HAVE_BZIP2_SUPPORT #cmakedefine01 HAVE_BZIP2_SUPPORT
/* Set to 1 if the libbz2 functions need the BZ2_ prefix */
#cmakedefine01 NEED_BZ2_PREFIX
/* Set to 1 if you have xz */ /* Set to 1 if you have xz */
#cmakedefine01 HAVE_XZ_SUPPORT #cmakedefine01 HAVE_XZ_SUPPORT

View file

@ -1,205 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000-2005 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kbzip2filter.h"
#include <config-compression.h>
#if HAVE_BZIP2_SUPPORT
// we don't need that
#define BZ_NO_STDIO
extern "C" {
#include <bzlib.h>
}
#if NEED_BZ2_PREFIX
#define bzDecompressInit(x,y,z) BZ2_bzDecompressInit(x,y,z)
#define bzDecompressEnd(x) BZ2_bzDecompressEnd(x)
#define bzCompressEnd(x) BZ2_bzCompressEnd(x)
#define bzDecompress(x) BZ2_bzDecompress(x)
#define bzCompress(x,y) BZ2_bzCompress(x, y)
#define bzCompressInit(x,y,z,a) BZ2_bzCompressInit(x, y, z, a);
#endif
#include <kdebug.h>
#include <qiodevice.h>
// For docu on this, see /usr/doc/bzip2-0.9.5d/bzip2-0.9.5d/manual_3.html
class KBzip2Filter::Private
{
public:
Private()
: isInitialized(false)
{
memset(&zStream, 0, sizeof(zStream));
mode = 0;
}
bz_stream zStream;
int mode;
bool isInitialized;
};
KBzip2Filter::KBzip2Filter()
:d(new Private)
{
}
KBzip2Filter::~KBzip2Filter()
{
delete d;
}
bool KBzip2Filter::init( int mode )
{
if (d->isInitialized) {
terminate();
}
d->zStream.next_in = 0;
d->zStream.avail_in = 0;
if ( mode == QIODevice::ReadOnly )
{
const int result = bzDecompressInit(&d->zStream, 0, 0);
if (result != BZ_OK) {
kDebug(7118) << "bzDecompressInit returned " << result;
return false;
}
} else if ( mode == QIODevice::WriteOnly ) {
const int result = bzCompressInit(&d->zStream, 5, 0, 0);
if (result != BZ_OK) {
kDebug(7118) << "bzDecompressInit returned " << result;
return false;
}
} else {
kWarning(7118) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
return false;
}
d->mode = mode;
d->isInitialized = true;
return true;
}
int KBzip2Filter::mode() const
{
return d->mode;
}
bool KBzip2Filter::terminate()
{
if (d->mode == QIODevice::ReadOnly) {
const int result = bzDecompressEnd(&d->zStream);
if (result != BZ_OK) {
kDebug(7118) << "bzDecompressEnd returned " << result;
return false;
}
} else if (d->mode == QIODevice::WriteOnly) {
const int result = bzCompressEnd(&d->zStream);
if (result != BZ_OK) {
kDebug(7118) << "bzDecompressEnd returned " << result;
return false;
}
} else {
kWarning(7118) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
return false;
}
d->isInitialized = false;
return true;
}
bool KBzip2Filter::reset()
{
// bzip2 doesn't seem to have a reset call...
if (!terminate()) {
return false;
}
if (!init( d->mode )) {
return false;
}
return true;
}
void KBzip2Filter::setOutBuffer( char * data, uint maxlen )
{
d->zStream.avail_out = maxlen;
d->zStream.next_out = data;
}
void KBzip2Filter::setInBuffer( const char *data, unsigned int size )
{
d->zStream.avail_in = size;
d->zStream.next_in = const_cast<char *>(data);
}
int KBzip2Filter::inBufferAvailable() const
{
return d->zStream.avail_in;
}
int KBzip2Filter::outBufferAvailable() const
{
return d->zStream.avail_out;
}
KBzip2Filter::Result KBzip2Filter::uncompress()
{
//qDebug() << "Calling bzDecompress with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
int result = bzDecompress(&d->zStream);
if ( result < BZ_OK ) {
kWarning(7118) << "bzDecompress returned" << result;
}
switch (result) {
case BZ_OK:
return KFilterBase::Ok;
case BZ_STREAM_END:
return KFilterBase::End;
default:
return KFilterBase::Error;
}
}
KBzip2Filter::Result KBzip2Filter::compress( bool finish )
{
//qDebug() << "Calling bzCompress with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
int result = bzCompress(&d->zStream, finish ? BZ_FINISH : BZ_RUN );
switch (result) {
case BZ_OK:
case BZ_FLUSH_OK:
case BZ_RUN_OK:
case BZ_FINISH_OK:
return KFilterBase::Ok;
case BZ_STREAM_END:
//qDebug() << " bzCompress returned " << result;
return KFilterBase::End;
default:
//qDebug() << " bzCompress returned " << result;
return KFilterBase::Error;
}
}
#endif /* HAVE_BZIP2_SUPPORT */

View file

@ -1,58 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KBZIP2FILTER_H
#define KBZIP2FILTER_H
#include <config-compression.h>
#if HAVE_BZIP2_SUPPORT
#include "kfilterbase.h"
/**
* Internal class used by KFilterDev
* @internal
*/
class KBzip2Filter : public KFilterBase
{
public:
KBzip2Filter();
virtual ~KBzip2Filter();
virtual bool init( int );
virtual int mode() const;
virtual bool terminate();
virtual bool reset();
virtual bool readHeader() { return true; } // bzip2 handles it by itself ! Cool !
virtual bool writeHeader( const QByteArray & ) { return true; }
virtual void setOutBuffer( char * data, uint maxlen );
virtual void setInBuffer( const char * data, uint size );
virtual int inBufferAvailable() const;
virtual int outBufferAvailable() const;
virtual Result uncompress();
virtual Result compress( bool finish );
private:
class Private;
Private* const d;
};
#endif
#endif

View file

@ -0,0 +1,290 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "config-compression.h"
#include "kcompressor.h"
#include "klocale.h"
#include "kmimetype.h"
#include "kdebug.h"
#include <qplatformdefs.h>
#include <libdeflate.h>
#if defined(HAVE_BZIP2_SUPPORT)
# include <bzlib.h>
#endif
#if defined(HAVE_XZ_SUPPORT)
# include <lzma.h>
#endif
// for reference:
// http://linux.math.tifr.res.in/manuals/html/manual_3.html
class KCompressorPrivate
{
public:
KCompressorPrivate();
KCompressor::KCompressorType m_type;
int m_level;
QByteArray m_result;
QString m_errorstring;
};
KCompressorPrivate::KCompressorPrivate()
: m_type(KCompressor::TypeUnknown),
m_level(1)
{
}
KCompressor::KCompressor()
: d(new KCompressorPrivate())
{
}
KCompressor::~KCompressor()
{
delete d;
}
KCompressor::KCompressorType KCompressor::type() const
{
return d->m_type;
}
bool KCompressor::setType(const KCompressorType type)
{
if (type == KCompressor::TypeUnknown) {
return false;
}
d->m_type = type;
return true;
}
int KCompressor::level() const
{
return d->m_level;
}
bool KCompressor::setLevel(const int level)
{
d->m_errorstring.clear();
if (level < 0 || level > 9) {
d->m_errorstring = i18n("Compression level not in the 0-9 range: %1", level);
return false;
}
d->m_level = level;
return true;
}
bool KCompressor::process(const QByteArray &data)
{
d->m_errorstring.clear();
d->m_result.clear();
switch (d->m_type) {
case KCompressor::TypeUnknown: {
return false;
}
case KCompressor::TypeDeflate: {
struct libdeflate_compressor* comp = libdeflate_alloc_compressor(d->m_level);
if (Q_UNLIKELY(!comp)) {
d->m_errorstring = i18n("Could not allocate compressor");
return false;
}
const size_t boundresult = libdeflate_deflate_compress_bound(comp, data.size());
if (Q_UNLIKELY(boundresult <= 0)) {
d->m_errorstring = i18n("Compression boundary is negative or zero");
libdeflate_free_compressor(comp);
return false;
}
d->m_result.resize(boundresult);
const size_t compresult = libdeflate_deflate_compress(
comp,
data.constData(), data.size(),
d->m_result.data(), d->m_result.size()
);
libdeflate_free_compressor(comp);
if (Q_UNLIKELY(compresult <= 0)) {
d->m_errorstring = i18n("Could not compress data");
d->m_result.clear();
return false;
}
d->m_result.resize(compresult);
return true;
}
case KCompressor::TypeZlib: {
struct libdeflate_compressor* comp = libdeflate_alloc_compressor(d->m_level);
if (Q_UNLIKELY(!comp)) {
d->m_errorstring = i18n("Could not allocate compressor");
return false;
}
const size_t boundresult = libdeflate_zlib_compress_bound(comp, data.size());
if (Q_UNLIKELY(boundresult <= 0)) {
d->m_errorstring = i18n("Compression boundary is negative or zero");
libdeflate_free_compressor(comp);
return false;
}
d->m_result.resize(boundresult);
const size_t compresult = libdeflate_zlib_compress(
comp,
data.constData(), data.size(),
d->m_result.data(), d->m_result.size()
);
libdeflate_free_compressor(comp);
if (Q_UNLIKELY(compresult <= 0)) {
d->m_errorstring = i18n("Could not compress data");
d->m_result.clear();
return false;
}
d->m_result.resize(compresult);
return true;
}
case KCompressor::TypeGZip: {
struct libdeflate_compressor* comp = libdeflate_alloc_compressor(d->m_level);
if (Q_UNLIKELY(!comp)) {
d->m_errorstring = i18n("Could not allocate compressor");
return false;
}
const size_t boundresult = libdeflate_gzip_compress_bound(comp, data.size());
if (Q_UNLIKELY(boundresult <= 0)) {
d->m_errorstring = i18n("Compression boundary is negative or zero");
libdeflate_free_compressor(comp);
return false;
}
d->m_result.resize(boundresult);
const size_t compresult = libdeflate_gzip_compress(
comp,
data.constData(), data.size(),
d->m_result.data(), d->m_result.size()
);
libdeflate_free_compressor(comp);
if (Q_UNLIKELY(compresult <= 0)) {
d->m_errorstring = i18n("Could not compress data");
d->m_result.clear();
return false;
}
d->m_result.resize(compresult);
return true;
}
#if defined(HAVE_BZIP2_SUPPORT)
case KCompressor::TypeBZip2: {
d->m_result.resize(data.size() + QT_BUFFSIZE);
uint compsize = d->m_result.size();
int compresult = compresult = BZ2_bzBuffToBuffCompress(
d->m_result.data(), &compsize,
(char*)data.constData(), data.size(),
d->m_level, 0, 0
);
if (Q_UNLIKELY(compresult < BZ_OK || compresult > BZ_STREAM_END)) {
d->m_errorstring = i18n("Could not compress data");
return false;
}
d->m_result.resize(compsize);
return true;
}
#endif // HAVE_BZIP2_SUPPORT
#if defined(HAVE_XZ_SUPPORT)
case KCompressor::TypeXZ: {
d->m_result.resize(data.size() + QT_BUFFSIZE);
size_t compsize = d->m_result.size();
lzma_stream comp = LZMA_STREAM_INIT;
comp.next_in = (const uint8_t*)data.constData();
comp.avail_in = data.size();
comp.next_out = (uint8_t*)d->m_result.data();
comp.avail_out = compsize;
lzma_ret compresult = lzma_easy_encoder(&comp, d->m_level, LZMA_CHECK_CRC32);
if (Q_UNLIKELY(compresult != LZMA_OK)) {
d->m_errorstring = i18n("Could not initialize compressor");
d->m_result.clear();
return false;
}
compresult = lzma_code(&comp, LZMA_FINISH);
if (Q_UNLIKELY(compresult != LZMA_OK && compresult != LZMA_STREAM_END)) {
d->m_errorstring = i18n("Could not compress data");
d->m_result.clear();
return false;
}
compsize = comp.total_out;
lzma_end(&comp);
d->m_result.resize(compsize);
return true;
}
#endif // HAVE_XZ_SUPPORT
default: {
kWarning() << "Unsupported type" << d->m_type;
return false;
}
}
Q_UNREACHABLE();
}
QByteArray KCompressor::result() const
{
return d->m_result;
}
QString KCompressor::errorString() const
{
return d->m_errorstring;
}
KCompressor::KCompressorType KCompressor::typeForMime(const QString &mime)
{
const KMimeType::Ptr kmimetype = KMimeType::mimeType(mime);
if (kmimetype) {
if (kmimetype->is(QString::fromLatin1("application/x-gzip"))) {
return KCompressor::TypeGZip;
} else if (kmimetype->is(QString::fromLatin1("application/x-bzip"))) {
return KCompressor::TypeBZip2;
} else if (kmimetype->is(QString::fromLatin1("application/x-xz"))) {
return KCompressor::TypeXZ;
}
}
return KCompressor::TypeUnknown;
}
KCompressor::KCompressorType KCompressor::typeForFile(const QString &filepath)
{
const KMimeType::Ptr kmimetype = KMimeType::findByPath(filepath);
if (kmimetype) {
return KCompressor::typeForMime(kmimetype->name());
}
return KCompressor::TypeUnknown;
}

View file

@ -0,0 +1,60 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KCOMPRESSOR_H
#define KCOMPRESSOR_H
#include <kdecore_export.h>
#include <QString>
class KCompressorPrivate;
class KDECORE_EXPORT KCompressor
{
public:
enum KCompressorType {
TypeUnknown = 0,
TypeDeflate = 1,
TypeZlib = 2,
TypeGZip = 3,
TypeBZip2 = 4,
TypeXZ = 5
};
KCompressor();
~KCompressor();
KCompressorType type() const;
bool setType(const KCompressorType type);
int level() const;
bool setLevel(const int level);
bool process(const QByteArray &data);
QByteArray result() const;
QString errorString() const;
static KCompressorType typeForMime(const QString &mime);
static KCompressorType typeForFile(const QString &filepath);
private:
KCompressorPrivate* d;
};
#endif // KCOMPRESSOR_H

View file

@ -0,0 +1,323 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "config-compression.h"
#include "kdecompressor.h"
#include "klocale.h"
#include "kmimetype.h"
#include "kdebug.h"
#include <qplatformdefs.h>
#include <limits.h>
#include <libdeflate.h>
#if defined(HAVE_BZIP2_SUPPORT)
# include <bzlib.h>
#endif
#if defined(HAVE_XZ_SUPPORT)
# include <lzma.h>
#endif
// for reference:
// http://linux.math.tifr.res.in/manuals/html/manual_3.html
class KDecompressorPrivate
{
public:
KDecompressorPrivate();
KDecompressor::KDecompressorType m_type;
QByteArray m_result;
QString m_errorstring;
};
KDecompressorPrivate::KDecompressorPrivate()
: m_type(KDecompressor::TypeUnknown)
{
}
KDecompressor::KDecompressor()
: d(new KDecompressorPrivate())
{
}
KDecompressor::~KDecompressor()
{
delete d;
}
KDecompressor::KDecompressorType KDecompressor::type() const
{
return d->m_type;
}
bool KDecompressor::setType(const KDecompressorType type)
{
if (type == KDecompressor::TypeUnknown) {
return false;
}
d->m_type = type;
return true;
}
bool KDecompressor::process(const QByteArray &data)
{
d->m_errorstring.clear();
d->m_result.clear();
switch (d->m_type) {
case KDecompressor::TypeUnknown: {
return false;
}
case KDecompressor::TypeDeflate: {
struct libdeflate_decompressor* decomp = libdeflate_alloc_decompressor();
if (Q_UNLIKELY(!decomp)) {
d->m_errorstring = i18n("Could not allocate decompressor");
return false;
}
size_t speculativesize = (data.size() * 2);
d->m_result.resize(speculativesize);
libdeflate_result decompresult = LIBDEFLATE_INSUFFICIENT_SPACE;
while (decompresult == LIBDEFLATE_INSUFFICIENT_SPACE) {
decompresult = libdeflate_deflate_decompress(
decomp,
data.constData(), data.size(),
d->m_result.data(), d->m_result.size(),
&speculativesize
);
if (decompresult == LIBDEFLATE_INSUFFICIENT_SPACE) {
speculativesize = (speculativesize + QT_BUFFSIZE);
d->m_result.resize(speculativesize);
}
if (speculativesize >= INT_MAX) {
break;
}
}
libdeflate_free_decompressor(decomp);
if (decompresult != LIBDEFLATE_SUCCESS) {
d->m_errorstring = i18n("Could not decompress data");
d->m_result.clear();
return false;
}
d->m_result.resize(speculativesize);
return true;
}
case KDecompressor::TypeZlib: {
struct libdeflate_decompressor* decomp = libdeflate_alloc_decompressor();
if (Q_UNLIKELY(!decomp)) {
d->m_errorstring = i18n("Could not allocate decompressor");
return false;
}
size_t speculativesize = (data.size() * 2);
d->m_result.resize(speculativesize);
libdeflate_result decompresult = LIBDEFLATE_INSUFFICIENT_SPACE;
while (decompresult == LIBDEFLATE_INSUFFICIENT_SPACE) {
decompresult = libdeflate_zlib_decompress(
decomp,
data.constData(), data.size(),
d->m_result.data(), d->m_result.size(),
&speculativesize
);
if (decompresult == LIBDEFLATE_INSUFFICIENT_SPACE) {
speculativesize = (speculativesize + QT_BUFFSIZE);
d->m_result.resize(speculativesize);
}
if (speculativesize >= INT_MAX) {
break;
}
}
libdeflate_free_decompressor(decomp);
if (decompresult != LIBDEFLATE_SUCCESS) {
d->m_errorstring = i18n("Could not decompress data");
d->m_result.clear();
return false;
}
d->m_result.resize(speculativesize);
return true;
}
case KDecompressor::TypeGZip: {
struct libdeflate_decompressor* decomp = libdeflate_alloc_decompressor();
if (Q_UNLIKELY(!decomp)) {
d->m_errorstring = i18n("Could not allocate decompressor");
return false;
}
size_t speculativesize = (data.size() * 2);
d->m_result.resize(speculativesize);
libdeflate_result decompresult = LIBDEFLATE_INSUFFICIENT_SPACE;
while (decompresult == LIBDEFLATE_INSUFFICIENT_SPACE) {
decompresult = libdeflate_gzip_decompress(
decomp,
data.constData(), data.size(),
d->m_result.data(), d->m_result.size(),
&speculativesize
);
if (decompresult == LIBDEFLATE_INSUFFICIENT_SPACE) {
speculativesize = (speculativesize + QT_BUFFSIZE);
d->m_result.resize(speculativesize);
}
if (speculativesize >= INT_MAX) {
break;
}
}
libdeflate_free_decompressor(decomp);
if (decompresult != LIBDEFLATE_SUCCESS) {
d->m_errorstring = i18n("Could not decompress data");
d->m_result.clear();
return false;
}
d->m_result.resize(speculativesize);
return true;
}
#if defined(HAVE_BZIP2_SUPPORT)
case KDecompressor::TypeBZip2: {
uint speculativesize = (data.size() * 2);
d->m_result.resize(speculativesize);
int decompresult = BZ_OUTBUFF_FULL;
while (decompresult == BZ_OUTBUFF_FULL) {
decompresult = BZ2_bzBuffToBuffDecompress(
d->m_result.data(), &speculativesize,
(char*)data.constData(), data.size(),
0, 0
);
if (decompresult == BZ_OUTBUFF_FULL) {
speculativesize = (speculativesize + QT_BUFFSIZE);
d->m_result.resize(speculativesize);
}
if (speculativesize >= INT_MAX) {
break;
}
}
if (Q_UNLIKELY(decompresult < BZ_OK || decompresult > BZ_STREAM_END)) {
d->m_errorstring = i18n("Could not decompress data");
d->m_result.clear();
return false;
}
d->m_result.resize(speculativesize);
return true;
}
#endif // HAVE_BZIP2_SUPPORT
#if defined(HAVE_XZ_SUPPORT)
case KDecompressor::TypeXZ: {
size_t speculativesize = (data.size() * 2);
d->m_result.resize(speculativesize);
lzma_stream decomp = LZMA_STREAM_INIT;
decomp.next_in = (const uint8_t*)data.constData();
decomp.avail_in = data.size();
decomp.next_out = (uint8_t*)d->m_result.data();
decomp.avail_out = speculativesize;
lzma_ret decompresult = lzma_auto_decoder(&decomp, UINT64_MAX, 0);
if (Q_UNLIKELY(decompresult != LZMA_OK)) {
d->m_errorstring = i18n("Could not initialize decompressor");
d->m_result.clear();
return false;
}
decompresult = LZMA_MEM_ERROR;
while (decompresult == LZMA_MEM_ERROR) {
decompresult = lzma_code(&decomp, LZMA_FINISH);
if (decompresult == LZMA_MEM_ERROR) {
speculativesize = (speculativesize + QT_BUFFSIZE);
d->m_result.resize(speculativesize);
}
if (speculativesize >= INT_MAX) {
break;
}
}
if (Q_UNLIKELY(decompresult != LZMA_OK && decompresult != LZMA_STREAM_END)) {
d->m_errorstring = i18n("Could not decompress data");
d->m_result.clear();
return false;
}
speculativesize = decomp.total_out;
lzma_end(&decomp);
d->m_result.resize(speculativesize);
return true;
}
#endif // HAVE_XZ_SUPPORT
default: {
kWarning() << "Unsupported type" << d->m_type;
return false;
}
}
Q_UNREACHABLE();
}
QByteArray KDecompressor::result() const
{
return d->m_result;
}
QString KDecompressor::errorString() const
{
return d->m_errorstring;
}
KDecompressor::KDecompressorType KDecompressor::typeForMime(const QString &mime)
{
const KMimeType::Ptr kmimetype = KMimeType::mimeType(mime);
if (kmimetype) {
if (kmimetype->is(QString::fromLatin1("application/x-gzip"))) {
return KDecompressor::TypeGZip;
} else if (kmimetype->is(QString::fromLatin1("application/x-bzip"))) {
return KDecompressor::TypeBZip2;
} else if (kmimetype->is(QString::fromLatin1("application/x-xz"))) {
return KDecompressor::TypeXZ;
}
}
return KDecompressor::TypeUnknown;
}
KDecompressor::KDecompressorType KDecompressor::typeForFile(const QString &filepath)
{
const KMimeType::Ptr kmimetype = KMimeType::findByPath(filepath);
if (kmimetype) {
return KDecompressor::typeForMime(kmimetype->name());
}
return KDecompressor::TypeUnknown;
}

View file

@ -0,0 +1,58 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDECOMPRESSOR_H
#define KDECOMPRESSOR_H
#include <kdecore_export.h>
#include <QString>
class KDecompressorPrivate;
class KDECORE_EXPORT KDecompressor
{
public:
enum KDecompressorType {
TypeUnknown = 0,
TypeDeflate = 1,
TypeZlib = 2,
TypeGZip = 3,
TypeBZip2 = 4,
TypeXZ = 5
};
KDecompressor();
~KDecompressor();
KDecompressorType type() const;
bool setType(const KDecompressorType type);
bool process(const QByteArray &data);
QByteArray result() const;
QString errorString() const;
static KDecompressorType typeForMime(const QString &mime);
static KDecompressorType typeForFile(const QString &filepath);
private:
KDecompressorPrivate* d;
};
#endif // KDECOMPRESSOR_H

View file

@ -1,176 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000-2005 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kfilterbase.h"
#include <config-compression.h>
#include <QtCore/QIODevice>
#include <kmimetype.h>
#include "kgzipfilter.h"
#if HAVE_BZIP2_SUPPORT
#include "kbzip2filter.h"
#endif
#if HAVE_XZ_SUPPORT
#include "kxzfilter.h"
#endif
class KFilterBasePrivate
{
public:
KFilterBasePrivate();
QIODevice * m_dev;
bool m_bAutoDel;
KFilterBase::FilterFlags m_flags;
};
KFilterBasePrivate::KFilterBasePrivate()
: m_dev( 0L ),
m_bAutoDel( false ),
m_flags(KFilterBase::WithHeaders)
{
}
KFilterBase::KFilterBase()
: d(new KFilterBasePrivate())
{
}
KFilterBase::~KFilterBase()
{
if ( d->m_bAutoDel )
delete d->m_dev;
delete d;
}
void KFilterBase::setDevice( QIODevice * dev, bool autodelete )
{
d->m_dev = dev;
d->m_bAutoDel = autodelete;
}
QIODevice * KFilterBase::device()
{
return d->m_dev;
}
bool KFilterBase::inBufferEmpty() const
{
return inBufferAvailable() == 0;
}
bool KFilterBase::outBufferFull() const
{
return outBufferAvailable() == 0;
}
KFilterBase * KFilterBase::findFilterByFileName( const QString & fileName )
{
if ( fileName.endsWith( QLatin1String(".gz"), Qt::CaseInsensitive ) )
{
return new KGzipFilter;
}
#if HAVE_BZIP2_SUPPORT
if ( fileName.endsWith( QLatin1String(".bz2"), Qt::CaseInsensitive ) )
{
return new KBzip2Filter;
}
#endif
#if HAVE_XZ_SUPPORT
if ( fileName.endsWith( QLatin1String(".lzma"), Qt::CaseInsensitive ) || fileName.endsWith( QLatin1String(".xz"), Qt::CaseInsensitive ) )
{
return new KXzFilter;
}
#endif
else
{
// not a warning, since this is called often with other mimetypes (see #88574)...
// maybe we can avoid that though?
//qDebug() << "KFilterBase::findFilterByFileName : no filter found for " << fileName;
}
return 0;
}
KFilterBase * KFilterBase::findFilterByMimeType( const QString & mimeType )
{
if (mimeType == QLatin1String("application/x-gzip")) {
return new KGzipFilter;
}
#if HAVE_BZIP2_SUPPORT
if (mimeType == QLatin1String("application/x-bzip")
|| mimeType == QLatin1String("application/x-bzip2") // old name, kept for compatibility
) {
return new KBzip2Filter;
}
#endif
#if HAVE_XZ_SUPPORT
if ( mimeType == QLatin1String( "application/x-lzma" ) // legacy name, still used
|| mimeType == QLatin1String( "application/x-xz" ) // current naming
) {
return new KXzFilter;
}
#endif
const KMimeType::Ptr mime = KMimeType::mimeType(mimeType);
if (mime) {
if (mime->is(QString::fromLatin1("application/x-gzip"))) {
return new KGzipFilter;
}
#if HAVE_BZIP2_SUPPORT
if (mime->is(QString::fromLatin1("application/x-bzip"))) {
return new KBzip2Filter;
}
#endif
#if HAVE_XZ_SUPPORT
if (mime->is(QString::fromLatin1("application/x-lzma"))) {
return new KXzFilter;
}
if (mime->is(QString::fromLatin1("application/x-xz"))) {
return new KXzFilter;
}
#endif
}
// not a warning, since this is called often with other mimetypes (see #88574)...
// maybe we can avoid that though?
//qDebug() << "no filter found for" << mimeType;
return 0;
}
bool KFilterBase::terminate()
{
return false;
}
bool KFilterBase::reset()
{
return false;
}
void KFilterBase::setFilterFlags(FilterFlags flags)
{
d->m_flags = flags;
}
KFilterBase::FilterFlags KFilterBase::filterFlags() const
{
return d->m_flags;
}

View file

@ -1,123 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KFILTERBASE_H
#define KFILTERBASE_H
#include <kdecore_export.h>
#include <QtCore/qiodevice.h>
class KFilterBasePrivate;
/**
* This is the base class for compression filters
* such as gzip and bzip2. It's pretty much internal.
* Don't use directly, use KFilterDev instead.
* @internal
*/
class KDECORE_EXPORT KFilterBase
{
public:
KFilterBase();
virtual ~KFilterBase();
/**
* Sets the device on which the filter will work
* @param dev the device on which the filter will work
* @param autodelete if true, @p dev is deleted when the filter is deleted
*/
void setDevice( QIODevice * dev, bool autodelete = false );
// Note that this isn't in the constructor, because of KPluginFactory::create,
// but it should be called before using the filterbase !
/**
* Returns the device on which the filter will work.
* @returns the device on which the filter will work
*/
QIODevice * device();
/** \internal */
virtual bool init( int mode ) = 0;
/** \internal */
virtual int mode() const = 0;
/** \internal */
virtual bool terminate();
/** \internal */
virtual bool reset();
/** \internal */
virtual bool readHeader() = 0;
/** \internal */
virtual bool writeHeader( const QByteArray & filename ) = 0;
/** \internal */
virtual void setOutBuffer( char * data, uint maxlen ) = 0;
/** \internal */
virtual void setInBuffer( const char * data, uint size ) = 0;
/** \internal */
virtual bool inBufferEmpty() const;
/** \internal */
virtual int inBufferAvailable() const = 0;
/** \internal */
virtual bool outBufferFull() const;
/** \internal */
virtual int outBufferAvailable() const = 0;
/** \internal */
enum Result { Ok, End, Error };
/** \internal */
virtual Result uncompress() = 0;
/** \internal */
virtual Result compress( bool finish ) = 0;
/**
* \internal
* \since 4.3
*/
enum FilterFlags {
NoHeaders = 0,
WithHeaders = 1
};
/**
* \internal
* \since 4.3
*/
void setFilterFlags(FilterFlags flags);
FilterFlags filterFlags() const;
/**
* Call this to create the appropriate filter for the file
* named @p fileName.
* @param fileName the name of the file to filter
* @return the filter for the @p fileName, or 0 if not found
*/
static KFilterBase * findFilterByFileName( const QString & fileName );
/**
* Call this to create the appropriate filter for the mimetype
* @p mimeType. For instance application/x-gzip.
* @param mimeType the mime type of the file to filter
* @return the filter for the @p mimeType, or 0 if not found
*/
static KFilterBase * findFilterByMimeType( const QString & mimeType );
private:
Q_DISABLE_COPY( KFilterBase )
KFilterBasePrivate * const d;
};
#endif

View file

@ -1,368 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000, 2006 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kfilterdev.h"
#include "kfilterbase.h"
#include <stdio.h> // for EOF
#include <stdlib.h>
#include <assert.h>
#include <QDebug>
#include <QtCore/qfile.h>
#define BUFFER_SIZE 8*1024
class KFilterDev::Private
{
public:
Private() : bNeedHeader(true), bSkipHeaders(false),
autoDeleteFilterBase(false), bOpenedUnderlyingDevice(false),
bIgnoreData(false){}
bool bNeedHeader;
bool bSkipHeaders;
bool autoDeleteFilterBase;
bool bOpenedUnderlyingDevice;
bool bIgnoreData;
QByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing
QByteArray origFileName;
KFilterBase::Result result;
KFilterBase *filter;
};
KFilterDev::KFilterDev( KFilterBase * _filter, bool autoDeleteFilterBase )
: d(new Private)
{
assert(_filter);
d->filter = _filter;
d->autoDeleteFilterBase = autoDeleteFilterBase;
}
KFilterDev::~KFilterDev()
{
if ( isOpen() )
close();
if ( d->autoDeleteFilterBase )
delete d->filter;
delete d;
}
//static
QIODevice * KFilterDev::deviceForFile( const QString & fileName, const QString & mimetype,
bool forceFilter )
{
QFile * f = new QFile( fileName );
KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName )
: KFilterBase::findFilterByMimeType( mimetype );
if ( base )
{
base->setDevice(f, true);
return new KFilterDev(base, true);
}
if(!forceFilter)
return f;
else
{
delete f;
return 0L;
}
}
QIODevice * KFilterDev::device( QIODevice* inDevice, const QString & mimetype, bool autoDeleteInDevice )
{
if (inDevice==0)
return 0;
KFilterBase * base = KFilterBase::findFilterByMimeType(mimetype);
if ( base )
{
base->setDevice(inDevice, autoDeleteInDevice);
return new KFilterDev(base, true /* auto-delete "base" */);
}
return 0;
}
bool KFilterDev::open( QIODevice::OpenMode mode )
{
if (isOpen()) {
qWarning() << "KFilterDev::open: device is already open";
return true; // QFile returns false, but well, the device -is- open...
}
//kDebug(7005) << mode;
if ( mode == QIODevice::ReadOnly )
{
d->buffer.resize(0);
}
else
{
d->buffer.resize( BUFFER_SIZE );
d->filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
}
d->bNeedHeader = !d->bSkipHeaders;
d->filter->setFilterFlags(d->bSkipHeaders ? KFilterBase::NoHeaders : KFilterBase::WithHeaders);
d->filter->init( mode );
d->bOpenedUnderlyingDevice = !d->filter->device()->isOpen();
bool ret = d->bOpenedUnderlyingDevice ? d->filter->device()->open( mode ) : true;
d->result = KFilterBase::Ok;
if ( !ret )
qWarning() << "KFilterDev::open: Couldn't open underlying device";
else
setOpenMode( mode );
return ret;
}
void KFilterDev::close()
{
if ( !isOpen() )
return;
if ( d->filter->mode() == QIODevice::WriteOnly )
write( 0L, 0 ); // finish writing
//kDebug(7005) << "Calling terminate().";
d->filter->terminate();
if ( d->bOpenedUnderlyingDevice )
d->filter->device()->close();
setOpenMode( QIODevice::NotOpen );
}
bool KFilterDev::seek( qint64 pos )
{
qint64 ioIndex = this->pos(); // current position
if ( ioIndex == pos )
return true;
//kDebug(7005) << "seek(" << pos << ") called";
Q_ASSERT ( d->filter->mode() == QIODevice::ReadOnly );
if ( pos == 0 )
{
// We can forget about the cached data
d->bNeedHeader = !d->bSkipHeaders;
d->result = KFilterBase::Ok;
d->filter->setInBuffer(0L,0);
d->filter->reset();
QIODevice::seek(pos);
return d->filter->device()->reset();
}
if ( ioIndex > pos ) // we can start from here
pos = pos - ioIndex;
else
{
// we have to start from 0 ! Ugly and slow, but better than the previous
// solution (KTarGz was allocating everything into memory)
if (!seek(0)) // recursive
return false;
}
//kDebug(7005) << "reading " << pos << " dummy bytes";
QByteArray dummy( qMin( pos, (qint64)3*BUFFER_SIZE ), 0 );
d->bIgnoreData = true;
bool result = ( read( dummy.data(), pos ) == pos );
d->bIgnoreData = false;
QIODevice::seek(pos);
return result;
}
bool KFilterDev::atEnd() const
{
return (d->result == KFilterBase::End)
&& QIODevice::atEnd() // take QIODevice's internal buffer into account
&& d->filter->device()->atEnd();
}
qint64 KFilterDev::readData( char *data, qint64 maxlen )
{
Q_ASSERT ( d->filter->mode() == QIODevice::ReadOnly );
//kDebug(7005) << "maxlen=" << maxlen;
KFilterBase* filter = d->filter;
uint dataReceived = 0;
// We came to the end of the stream
if ( d->result == KFilterBase::End )
return dataReceived;
// If we had an error, return -1.
if ( d->result != KFilterBase::Ok )
return -1;
qint64 outBufferSize;
if ( d->bIgnoreData )
{
outBufferSize = qMin( maxlen, (qint64)3*BUFFER_SIZE );
}
else
{
outBufferSize = maxlen;
}
outBufferSize -= dataReceived;
qint64 availOut = outBufferSize;
filter->setOutBuffer( data, outBufferSize );
while ( dataReceived < maxlen )
{
if (filter->inBufferEmpty())
{
// Not sure about the best size to set there.
// For sure, it should be bigger than the header size (see comment in readHeader)
d->buffer.resize( BUFFER_SIZE );
// Request data from underlying device
int size = filter->device()->read( d->buffer.data(),
d->buffer.size() );
//kDebug(7005) << "got" << size << "bytes from device";
if (size) {
filter->setInBuffer( d->buffer.data(), size );
} else {
// Not enough data available in underlying device for now
break;
}
}
if (d->bNeedHeader)
{
(void) filter->readHeader();
d->bNeedHeader = false;
}
d->result = filter->uncompress();
if (d->result == KFilterBase::Error)
{
qWarning() << "KFilterDev: Error when uncompressing data";
break;
}
// We got that much data since the last time we went here
uint outReceived = availOut - filter->outBufferAvailable();
//kDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived;
if( availOut < (uint)filter->outBufferAvailable() )
qWarning() << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !";
dataReceived += outReceived;
if ( !d->bIgnoreData ) // Move on in the output buffer
{
data += outReceived;
availOut = maxlen - dataReceived;
}
else if ( maxlen - dataReceived < outBufferSize )
{
availOut = maxlen - dataReceived;
}
if (d->result == KFilterBase::End)
{
//kDebug(7005) << "got END. dataReceived=" << dataReceived;
break; // Finished.
}
filter->setOutBuffer( data, availOut );
}
return dataReceived;
}
qint64 KFilterDev::writeData( const char *data /*0 to finish*/, qint64 len )
{
KFilterBase* filter = d->filter;
Q_ASSERT ( filter->mode() == QIODevice::WriteOnly );
// If we had an error, return 0.
if ( d->result != KFilterBase::Ok )
return 0;
bool finish = (data == 0L);
if (!finish)
{
filter->setInBuffer( data, len );
if (d->bNeedHeader)
{
(void)filter->writeHeader( d->origFileName );
d->bNeedHeader = false;
}
}
uint dataWritten = 0;
uint availIn = len;
while ( dataWritten < len || finish )
{
d->result = filter->compress( finish );
if (d->result == KFilterBase::Error)
{
qWarning() << "KFilterDev: Error when compressing data";
// What to do ?
break;
}
// Wrote everything ?
if (filter->inBufferEmpty() || (d->result == KFilterBase::End))
{
// We got that much data since the last time we went here
uint wrote = availIn - filter->inBufferAvailable();
//kDebug(7005) << " Wrote everything for now. avail_in=" << filter->inBufferAvailable() << "result=" << d->result << "wrote=" << wrote;
// Move on in the input buffer
data += wrote;
dataWritten += wrote;
availIn = len - dataWritten;
//kDebug(7005) << " availIn=" << availIn << "dataWritten=" << dataWritten << "pos=" << pos();
if ( availIn > 0 )
filter->setInBuffer( data, availIn );
}
if (filter->outBufferFull() || (d->result == KFilterBase::End) || finish)
{
//kDebug(7005) << " writing to underlying. avail_out=" << filter->outBufferAvailable();
int towrite = d->buffer.size() - filter->outBufferAvailable();
if ( towrite > 0 )
{
// Write compressed data to underlying device
int size = filter->device()->write( d->buffer.data(), towrite );
if ( size != towrite ) {
qWarning() << "KFilterDev::write. Could only write " << size << " out of " << towrite << " bytes";
return 0; // indicate an error (happens on disk full)
}
//else
//kDebug(7005) << " wrote " << size << " bytes";
}
if (d->result == KFilterBase::End)
{
//kDebug(7005) << " END";
Q_ASSERT(finish); // hopefully we don't get end before finishing
break;
}
d->buffer.resize(BUFFER_SIZE);
filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
}
}
return dataWritten;
}
void KFilterDev::setOrigFileName( const QByteArray & fileName )
{
d->origFileName = fileName;
}
void KFilterDev::setSkipHeaders()
{
d->bSkipHeaders = true;
}

View file

@ -1,151 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KFILTERDEV_H
#define KFILTERDEV_H
#include <kdecore_export.h>
#include <QtCore/qfile.h>
class KFilterBase;
/**
* A class for reading and writing compressed data onto a device
* (e.g. file, but other usages are possible, like a buffer or a socket).
*
* To simply read/write compressed files, see deviceForFile.
*
* @author David Faure <faure@kde.org>
*/
class KDECORE_EXPORT KFilterDev : public QIODevice
{
public:
/**
* Destructs the KFilterDev.
* Calls close() if the filter device is still open.
*/
virtual ~KFilterDev();
/**
* Open for reading or writing.
* If the KFilterBase's device is not opened, it will be opened.
*/
virtual bool open( QIODevice::OpenMode mode );
/**
* Close after reading or writing.
* If the KFilterBase's device was opened by open(), it will be closed.
*/
virtual void close();
/**
* For writing gzip compressed files only:
* set the name of the original file, to be used in the gzip header.
* @param fileName the name of the original file
*/
void setOrigFileName( const QByteArray & fileName );
/**
* Call this let this device skip the gzip headers when reading/writing.
* This way KFilterDev (with gzip filter) can be used as a direct wrapper
* around zlib - this is used by KZip.
*/
void setSkipHeaders();
/**
* That one can be quite slow, when going back. Use with care.
*/
virtual bool seek( qint64 );
virtual bool atEnd() const;
/// Reimplemented to return true. KFilterDev is a sequential QIODevice.
/// Well, not really, since it supports seeking and KZip uses that.
//virtual bool isSequential() const { return true; }
public:
// KDE4 TODO: turn those static methods into constructors
/**
* Creates an i/o device that is able to read from @p fileName,
* whether it's compressed or not. Available compression filters
* (gzip/bzip2 etc.) will automatically be used.
*
* The compression filter to be used is determined from the @p fileName
* if @p mimetype is empty. Pass "application/x-gzip" or "application/x-bzip"
* to force the corresponding decompression filter, if available.
*
* Warning: application/x-bzip may not be available.
* In that case a QFile opened on the compressed data will be returned !
* Use KFilterBase::findFilterByMimeType and code similar to what
* deviceForFile is doing, to better control what's happening.
*
* The returned QIODevice has to be deleted after using.
*
* @param fileName the name of the file to filter
* @param mimetype the mime type of the file to filter, or QString() if unknown
* @param forceFilter if true, the function will either find a compression filter, or return 0.
* If false, it will always return a QIODevice. If no
* filter is available it will return a simple QFile.
* This can be useful if the file is usable without a filter.
* @return if a filter has been found, the QIODevice for the filter. If the
* filter does not exist, the return value depends on @p forceFilter.
* The returned QIODevice has to be deleted after using.
*/
static QIODevice * deviceForFile( const QString & fileName, const QString & mimetype = QString(),
bool forceFilter = false );
/**
* Creates an i/o device that is able to read from the QIODevice @p inDevice,
* whether the data is compressed or not. Available compression filters
* (gzip/bzip2 etc.) will automatically be used.
*
* The compression filter to be used is determined @p mimetype .
* Pass "application/x-gzip" or "application/x-bzip"
* to use the corresponding decompression filter.
*
* Warning: application/x-bzip may not be available.
* In that case 0 will be returned !
*
* The returned QIODevice has to be deleted after using.
* @param inDevice input device. Won't be deleted if @p autoDeleteInDevice = false
* @param mimetype the mime type for the filter
* @param autoDeleteInDevice if true, @p inDevice will be deleted automatically
* @return a KFilterDev that filters the original stream. Must be deleted after using
*/
static QIODevice * device( QIODevice* inDevice, const QString & mimetype, bool autoDeleteInDevice = true );
protected:
virtual qint64 readData( char *data, qint64 maxlen );
virtual qint64 writeData( const char *data, qint64 len );
private:
/**
* Constructs a KFilterDev for a given filter (e.g. gzip, bzip2 etc.).
* @param filter the KFilterBase to use
* @param autoDeleteFilterBase when true this object will become the
* owner of @p filter.
*/
explicit KFilterDev( KFilterBase * filter, bool autoDeleteFilterBase = false );
private:
class Private;
Private* const d;
};
#endif

View file

@ -1,390 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000-2005 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kgzipfilter.h"
#include <kdebug.h>
#include <time.h>
#include <zlib.h>
#include <QtCore/qiodevice.h>
/* gzip flag byte */
#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 */
// #define DEBUG_GZIP
class KGzipFilter::Private
{
public:
Private()
: headerWritten(false), footerWritten(false), compressed(false), mode(0), crc(0), isInitialized(false)
{
zStream.zalloc = (alloc_func)0;
zStream.zfree = (free_func)0;
zStream.opaque = (voidpf)0;
}
z_stream zStream;
bool headerWritten;
bool footerWritten;
bool compressed;
int mode;
ulong crc;
bool isInitialized;
};
KGzipFilter::KGzipFilter()
: d(new Private)
{
}
KGzipFilter::~KGzipFilter()
{
delete d;
}
bool KGzipFilter::init(int mode)
{
return init(mode, filterFlags() == WithHeaders ? GZipHeader : RawDeflate);
}
bool KGzipFilter::init(int mode, Flag flag)
{
if (d->isInitialized) {
terminate();
}
d->zStream.next_in = Z_NULL;
d->zStream.avail_in = 0;
if ( mode == QIODevice::ReadOnly )
{
const int windowBits = (flag == RawDeflate)
? -MAX_WBITS /*no zlib header*/
: (flag == GZipHeader) ?
MAX_WBITS + 32 /* auto-detect and eat gzip header */
: MAX_WBITS /*zlib header*/;
const int result = inflateInit2(&d->zStream, windowBits);
if ( result != Z_OK ) {
kDebug(7110) << "inflateInit2 returned " << result;
return false;
}
} else if ( mode == QIODevice::WriteOnly )
{
int result = deflateInit2(&d->zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); // same here
if ( result != Z_OK ) {
kDebug(7110) << "deflateInit returned " << result;
return false;
}
} else {
kWarning(7110) << "KGzipFilter: Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
return false;
}
d->mode = mode;
d->compressed = true;
d->headerWritten = false;
d->footerWritten = false;
d->isInitialized = true;
return true;
}
int KGzipFilter::mode() const
{
return d->mode;
}
bool KGzipFilter::terminate()
{
if ( d->mode == QIODevice::ReadOnly )
{
int result = inflateEnd(&d->zStream);
if ( result != Z_OK ) {
kDebug(7110) << "inflateEnd returned " << result;
return false;
}
} else if ( d->mode == QIODevice::WriteOnly )
{
int result = deflateEnd(&d->zStream);
if ( result != Z_OK ) {
kDebug(7110) << "deflateEnd returned " << result;
return false;
}
}
d->isInitialized = false;
return true;
}
bool KGzipFilter::reset()
{
if ( d->mode == QIODevice::ReadOnly )
{
int result = inflateReset(&d->zStream);
if ( result != Z_OK ) {
kDebug(7110) << "inflateReset returned " << result;
return false;
}
} else if ( d->mode == QIODevice::WriteOnly ) {
int result = deflateReset(&d->zStream);
if ( result != Z_OK ) {
kDebug(7110) << "deflateReset returned " << result;
return false;
}
d->headerWritten = false;
d->footerWritten = false;
}
return true;
}
bool KGzipFilter::readHeader()
{
// We now rely on zlib to read the full header (see the MAX_WBITS + 32 in init).
// We just use this method to check if the data is actually compressed.
#ifdef DEBUG_GZIP
kDebug(7110) << "avail=" << d->zStream.avail_in;
#endif
// Assume not compressed until we see a gzip header
d->compressed = false;
Bytef *p = d->zStream.next_in;
int i = d->zStream.avail_in;
if ((i -= 10) < 0) return false; // Need at least 10 bytes
#ifdef DEBUG_GZIP
kDebug(7110) << "first byte is " << QString::number(*p,16);
#endif
if (*p++ != 0x1f) return false; // GZip magic
#ifdef DEBUG_GZIP
kDebug(7110) << "second byte is " << QString::number(*p,16);
#endif
if (*p++ != 0x8b) return false;
#if 0
int method = *p++;
int flags = *p++;
if ((method != Z_DEFLATED) || (flags & RESERVED) != 0) return false;
p += 6;
if ((flags & EXTRA_FIELD) != 0) // skip extra field
{
if ((i -= 2) < 0) return false; // Need at least 2 bytes
int len = *p++;
len += (*p++) << 8;
if ((i -= len) < 0) return false; // Need at least len bytes
p += len;
}
if ((flags & ORIG_NAME) != 0) // skip original file name
{
#ifdef DEBUG_GZIP
kDebug(7110) << "ORIG_NAME=" << (char*)p;
#endif
while( (i > 0) && (*p))
{
i--; p++;
}
if (--i <= 0) return false;
p++;
}
if ((flags & COMMENT) != 0) // skip comment
{
while( (i > 0) && (*p))
{
i--; p++;
}
if (--i <= 0) return false;
p++;
}
if ((flags & HEAD_CRC) != 0) // skip the header crc
{
if ((i-=2) < 0) return false;
p += 2;
}
d->zStream.avail_in = i;
d->zStream.next_in = p;
#endif
d->compressed = true;
#ifdef DEBUG_GZIP
kDebug(7110) << "header OK";
#endif
return true;
}
/* Output a 16 bit value, lsb first */
#define put_short(w) \
*p++ = (uchar) ((w) & 0xff); \
*p++ = (uchar) ((ushort)(w) >> 8);
/* Output a 32 bit value to the bit stream, lsb first */
#define put_long(n) \
put_short((n) & 0xffff); \
put_short(((ulong)(n)) >> 16);
bool KGzipFilter::writeHeader( const QByteArray & fileName )
{
Bytef *p = d->zStream.next_out;
int i = d->zStream.avail_out;
*p++ = 0x1f;
*p++ = 0x8b;
*p++ = Z_DEFLATED;
*p++ = ORIG_NAME;
put_long( time( 0L ) ); // Modification time (in unix format)
*p++ = 0; // Extra flags (2=max compress, 4=fastest compress)
*p++ = 3; // Unix
uint len = fileName.length();
for ( uint j = 0 ; j < len ; ++j )
*p++ = fileName[j];
*p++ = 0;
int headerSize = p - d->zStream.next_out;
i -= headerSize;
Q_ASSERT(i>0);
d->crc = crc32(0L, Z_NULL, 0);
d->zStream.next_out = p;
d->zStream.avail_out = i;
d->headerWritten = true;
return true;
}
void KGzipFilter::writeFooter()
{
Q_ASSERT( d->headerWritten );
Q_ASSERT(!d->footerWritten);
Bytef *p = d->zStream.next_out;
int i = d->zStream.avail_out;
//kDebug(7110) << "avail_out=" << i << "writing CRC=" << QString::number(d->crc, 16) << "at p=" << p;
put_long( d->crc );
//kDebug(7110) << "writing totalin=" << d->zStream.total_in << "at p=" << p;
put_long( d->zStream.total_in );
i -= p - d->zStream.next_out;
d->zStream.next_out = p;
d->zStream.avail_out = i;
d->footerWritten = true;
}
void KGzipFilter::setOutBuffer( char * data, uint maxlen )
{
d->zStream.avail_out = maxlen;
d->zStream.next_out = (Bytef *) data;
}
void KGzipFilter::setInBuffer( const char * data, uint size )
{
#ifdef DEBUG_GZIP
kDebug(7110) << "avail_in=" << size;
#endif
d->zStream.avail_in = size;
d->zStream.next_in = (Bytef*) data;
}
int KGzipFilter::inBufferAvailable() const
{
return d->zStream.avail_in;
}
int KGzipFilter::outBufferAvailable() const
{
return d->zStream.avail_out;
}
KGzipFilter::Result KGzipFilter::uncompress_noop()
{
// I'm not sure we really need support for that (uncompressed streams),
// but why not, it can't hurt to have it. One case I can think of is someone
// naming a tar file "blah.tar.gz" :-)
if ( d->zStream.avail_in > 0 )
{
int n = (d->zStream.avail_in < d->zStream.avail_out) ? d->zStream.avail_in : d->zStream.avail_out;
memcpy( d->zStream.next_out, d->zStream.next_in, n );
d->zStream.avail_out -= n;
d->zStream.next_in += n;
d->zStream.avail_in -= n;
return KFilterBase::Ok;
} else
return KFilterBase::End;
}
KGzipFilter::Result KGzipFilter::uncompress()
{
#ifndef NDEBUG
if (d->mode == 0) {
qWarning() << "mode==0; KGzipFilter::init was not called!";
return KFilterBase::Error;
} else if (d->mode == QIODevice::WriteOnly) {
qWarning() << "uncompress called but the filter was opened for writing!";
return KFilterBase::Error;
}
Q_ASSERT ( d->mode == QIODevice::ReadOnly );
#endif
if ( d->compressed )
{
#ifdef DEBUG_GZIP
kDebug(7110) << "Calling inflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
kDebug(7110) << " next_in=" << d->zStream.next_in;
#endif
int result = inflate(&d->zStream, Z_SYNC_FLUSH);
#ifdef DEBUG_GZIP
kDebug(7110) << " -> inflate returned " << result;
kDebug(7110) << " now avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
kDebug(7110) << " next_in=" << d->zStream.next_in;
#else
if ( result != Z_OK && result != Z_STREAM_END )
kDebug(7110) << "Warning: inflate() returned " << result;
#endif
return ( result == Z_OK ? KFilterBase::Ok : ( result == Z_STREAM_END ? KFilterBase::End : KFilterBase::Error ) );
} else
return uncompress_noop();
}
KGzipFilter::Result KGzipFilter::compress( bool finish )
{
Q_ASSERT ( d->compressed );
Q_ASSERT ( d->mode == QIODevice::WriteOnly );
Bytef* p = d->zStream.next_in;
ulong len = d->zStream.avail_in;
#ifdef DEBUG_GZIP
kDebug(7110) << " calling deflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
#endif
const int result = deflate(&d->zStream, finish ? Z_FINISH : Z_NO_FLUSH);
if ( result != Z_OK && result != Z_STREAM_END ) {
kDebug(7110) << " deflate returned " << result;
}
if ( d->headerWritten )
{
//kDebug(7110) << "Computing CRC for the next " << len - d->zStream.avail_in << " bytes";
d->crc = crc32(d->crc, p, len - d->zStream.avail_in);
}
KGzipFilter::Result callerResult = result == Z_OK ? KFilterBase::Ok : (Z_STREAM_END ? KFilterBase::End : KFilterBase::Error);
if (result == Z_STREAM_END && d->headerWritten && !d->footerWritten) {
if (d->zStream.avail_out >= 8 /*footer size*/) {
//kDebug(7110) << "finished, write footer";
writeFooter();
} else {
// No room to write the footer (#157706/#188415), we'll have to do it on the next pass.
//kDebug(7110) << "finished, but no room for footer yet";
callerResult = KFilterBase::Ok;
}
}
return callerResult;
}

View file

@ -1,73 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2000, 2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KGZIPFILTER_H
#define KGZIPFILTER_H
#include "kfilterbase.h"
/**
* Internal class used by KFilterDev
*
* This header is not installed.
*
* @internal
*/
class KDECORE_EXPORT KGzipFilter : public KFilterBase
{
public:
KGzipFilter();
virtual ~KGzipFilter();
virtual bool init(int mode);
// The top of zlib.h explains it: there are three cases.
// - Raw deflate, no header (e.g. inside a ZIP file)
// - Thin zlib header (1) (which is normally what HTTP calls "deflate" (2))
// - Gzip header, implemented here by readHeader
//
// (1) as written out by compress()/compress2()
// (2) see http://www.zlib.net/zlib_faq.html#faq39
enum Flag {
RawDeflate = 0, // raw deflate data
ZlibHeader = 1, // zlib headers (HTTP deflate)
GZipHeader = 2
};
bool init(int mode, Flag flag); // for direct users of KGzipFilter
virtual int mode() const;
virtual bool terminate();
virtual bool reset();
virtual bool readHeader(); // this is about the GZIP header
virtual bool writeHeader( const QByteArray & fileName );
void writeFooter();
virtual void setOutBuffer( char * data, uint maxlen );
virtual void setInBuffer( const char * data, uint size );
virtual int inBufferAvailable() const;
virtual int outBufferAvailable() const;
virtual Result uncompress();
virtual Result compress( bool finish );
private:
Result uncompress_noop();
class Private;
Private* const d;
};
#endif

View file

@ -1,184 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2007-2008 Per Øyvind Karlsen <peroyvind@mandriva.org>
Based on kbzip2filter:
Copyright (C) 2000-2005 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kxzfilter.h"
#include <config-compression.h>
#if HAVE_XZ_SUPPORT
extern "C" {
#include <lzma.h>
}
#include <kdebug.h>
#include <qiodevice.h>
class KXzFilter::Private
{
public:
Private()
: isInitialized(false)
{
memset(&zStream, 0, sizeof(zStream));
mode = 0;
}
lzma_stream zStream;
int mode;
bool isInitialized;
};
KXzFilter::KXzFilter()
:d(new Private)
{
}
KXzFilter::~KXzFilter()
{
delete d;
}
bool KXzFilter::init( int mode )
{
if (d->isInitialized) {
terminate();
}
d->zStream.next_in = 0;
d->zStream.avail_in = 0;
if ( mode == QIODevice::ReadOnly ) {
/* We set the memlimit for decompression to 100MiB which should be
* more than enough to be sufficient for level 9 which requires 65 MiB.
*/
const lzma_ret result = lzma_auto_decoder(&d->zStream, 100<<20, 0);
if (result != LZMA_OK) {
kDebug(7131) << "lzma_auto_decoder returned " << result;
return false;
}
} else if ( mode == QIODevice::WriteOnly ) {
const lzma_ret result = lzma_easy_encoder(&d->zStream, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32);
if (result != LZMA_OK) {
kDebug(7131) << "lzma_easy_encoder returned " << result;
return false;
}
} else {
kWarning(7131) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
return false;
}
d->mode = mode;
d->isInitialized = true;
return true;
}
int KXzFilter::mode() const
{
return d->mode;
}
bool KXzFilter::terminate()
{
if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) {
lzma_end(&d->zStream);
} else {
kWarning(7131) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
return false;
}
d->isInitialized = false;
return true;
}
bool KXzFilter::reset()
{
kDebug(7131) << "KXzFilter::reset";
// liblzma doesn't have a reset call...
if (!terminate()) {
return false;
}
if (!init( d->mode )) {
return false;
}
return true;
}
void KXzFilter::setOutBuffer( char * data, uint maxlen )
{
d->zStream.avail_out = maxlen;
d->zStream.next_out = (uint8_t *)data;
}
void KXzFilter::setInBuffer( const char *data, unsigned int size )
{
d->zStream.avail_in = size;
d->zStream.next_in = (uint8_t *)const_cast<char *>(data);
}
int KXzFilter::inBufferAvailable() const
{
return d->zStream.avail_in;
}
int KXzFilter::outBufferAvailable() const
{
return d->zStream.avail_out;
}
KXzFilter::Result KXzFilter::uncompress()
{
//kDebug(7131) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable();
lzma_ret result = lzma_code(&d->zStream, LZMA_RUN);
if ( result != LZMA_OK ) {
kDebug(7131) << "lzma_code returned " << result;
kDebug(7131) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error );
}
switch (result) {
case LZMA_OK:
return KFilterBase::Ok;
case LZMA_STREAM_END:
return KFilterBase::End;
default:
return KFilterBase::Error;
}
}
KXzFilter::Result KXzFilter::compress( bool finish )
{
//kDebug(7131) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
lzma_ret result = lzma_code(&d->zStream, finish ? LZMA_FINISH : LZMA_RUN );
switch (result) {
case LZMA_OK:
return KFilterBase::Ok;
case LZMA_STREAM_END:
kDebug(7131) << " lzma_code returned " << result;
return KFilterBase::End;
default:
kDebug(7131) << " lzma_code returned " << result;
return KFilterBase::Error;
}
}
#endif /* HAVE_XZ_SUPPORT */

View file

@ -1,61 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2007-2008 Per Øyvind Karlsen <peroyvind@mandriva.org>
Based on kbzip2filter:
Copyright (C) 2000 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KXZFILTER_H
#define KXZFILTER_H
#include <config-compression.h>
#if HAVE_XZ_SUPPORT
#include "kfilterbase.h"
/**
* Internal class used by KFilterDev
* @internal
*/
class KXzFilter : public KFilterBase
{
public:
KXzFilter();
virtual ~KXzFilter();
virtual bool init( int );
virtual int mode() const;
virtual bool terminate();
virtual bool reset();
virtual bool readHeader() { return true; } // lzma handles it by itself ! Cool !
virtual bool writeHeader( const QByteArray & ) { return true; }
virtual void setOutBuffer( char * data, uint maxlen );
virtual void setInBuffer( const char * data, uint size );
virtual int inBufferAvailable() const;
virtual int outBufferAvailable() const;
virtual Result uncompress();
virtual Result compress( bool finish );
private:
class Private;
Private* const d;
};
#endif
#endif // KXZFILTER_H

View file

@ -19,8 +19,6 @@
Boston, MA 02110-1301, USA. Boston, MA 02110-1301, USA.
*/ */
#include "kcharsets.h" #include "kcharsets.h"
#include "kfilterdev.h"
#include "kentities.cpp" #include "kentities.cpp"
#include "kconfig.h" #include "kconfig.h"

View file

@ -104,12 +104,6 @@ set(kmimeglobsfileparsertest_SRCS kmimeglobsfileparsertest.cpp ../services/kmime
kde4_add_test(kdecore-kmimeglobsfileparsertest ${kmimeglobsfileparsertest_SRCS}) kde4_add_test(kdecore-kmimeglobsfileparsertest ${kmimeglobsfileparsertest_SRCS})
target_link_libraries(kdecore-kmimeglobsfileparsertest ${KDE4_KDECORE_LIBS} ${QT_QTTEST_LIBRARY}) target_link_libraries(kdecore-kmimeglobsfileparsertest ${KDE4_KDECORE_LIBS} ${QT_QTTEST_LIBRARY})
########### kfiltertest ###############
set(kfiltertest_SRCS kfiltertest.cpp)
kde4_add_test(kdecore-kfiltertest ${kfiltertest_SRCS})
target_link_libraries(kdecore-kfiltertest ${KDE4_KDECORE_LIBS} ${QT_QTTEST_LIBRARY} ${ZLIB_LIBRARIES})
########### module for klibloadertest4 ############### ########### module for klibloadertest4 ###############
set(klibloadertestmodule4_PART_SRCS klibloadertest4_module.cpp ) set(klibloadertestmodule4_PART_SRCS klibloadertest4_module.cpp )

View file

@ -1,369 +0,0 @@
/*
* Copyright (C) 2002-2005 David Faure <faure@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation;
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kfiltertest.h"
#include "qtest_kde.h"
#include <config-compression.h>
#include "kfilterdev.h"
#include "kfilterbase.h"
#include <kdebug.h>
#include <kgzipfilter.h>
#include <krandom.h>
#include <QtCore/QFile>
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QBuffer>
#include <unistd.h>
#include <limits.h>
#include <zlib.h>
QTEST_KDEMAIN_CORE(KFilterTest)
void KFilterTest::initTestCase()
{
const QString currentdir = QDir::currentPath();
pathgz = currentdir + "/test.gz";
pathbz2 = currentdir + "/test.bz2";
pathxz = currentdir + "/test.xz";
// warning, update the COMPAREs in test_block_write() if changing the test data...
testData = "hello world\n";
}
void KFilterTest::test_block_write(const QString & fileName, const QByteArray& data)
{
QIODevice * dev = KFilterDev::deviceForFile( fileName );
QVERIFY( dev != 0 );
bool ok = dev->open( QIODevice::WriteOnly );
QVERIFY( ok );
const int ret = dev->write(data);
QCOMPARE(ret, data.size());
dev->close();
delete dev;
QVERIFY( QFile::exists( fileName ) );
}
void KFilterTest::test_block_write()
{
kDebug() << " -- test_block_write gzip -- ";
test_block_write(pathgz, testData);
QCOMPARE( QFileInfo( pathgz ).size(), 33LL ); // size of test.gz
#if HAVE_BZIP2_SUPPORT
kDebug() << " -- test_block_write bzip2 -- ";
test_block_write(pathbz2, testData);
QCOMPARE( QFileInfo( pathbz2 ).size(), 52LL ); // size of test.bz2
#endif
#if HAVE_XZ_SUPPORT
kDebug() << " -- test_block_write xz -- ";
test_block_write(pathxz, testData);
QCOMPARE( QFileInfo( pathxz ).size(), 64LL ); // size of test.lzma
#endif
}
void KFilterTest::test_biggerWrites()
{
const QString currentdir = QDir::currentPath();
const QString outFile = currentdir + "/test_big.gz";
// Find the out-of-bounds from #157706/#188415
QByteArray data;
data.reserve(10000);
// Prepare test data
for (int i = 0; i < 8170; ++i)
data.append(static_cast<char>(KRandom::randomMax(256)));
QCOMPARE(data.size(), 8170);
// 8170 random bytes compress to 8194 bytes due to the gzip header/footer.
// Now we can go one by one until we pass 8192.
// On 32 bit systems it crashed with data.size()=8173, before the "no room for footer yet" fix.
int compressedSize = 0;
while (compressedSize < 8200) {
test_block_write(outFile, data);
compressedSize = QFileInfo(outFile).size();
kDebug() << data.size() << "compressed into" << compressedSize;
// Test data is valid
test_readall(outFile, QString::fromLatin1("application/x-gzip"), data);
data.append(static_cast<char>(KRandom::randomMax(256)));
}
}
void KFilterTest::test_block_read( const QString & fileName )
{
QIODevice * dev = KFilterDev::deviceForFile( fileName );
QVERIFY( dev != 0 );
bool ok = dev->open( QIODevice::ReadOnly );
QVERIFY( ok );
QByteArray array(1024,'\0');
QByteArray read;
int n;
while ( ( n = dev->read( array.data(), array.size() ) ) )
{
QVERIFY( n > 0 );
read += QByteArray( array, n );
//kDebug() << "read returned " << n;
//kDebug() << "read='" << read << "'";
// pos() has no real meaning on sequential devices
// Ah, but kzip uses kfilterdev as a non-sequential device...
QCOMPARE( (int)dev->pos(), (int)read.size() );
//kDebug() << "dev.at = " << dev->at();
}
QCOMPARE( read, testData );
// Test seeking back
ok = dev->seek(0);
// test readAll
read = dev->readAll();
QCOMPARE( read.size(), testData.size() );
QCOMPARE( read, testData );
dev->close();
delete dev;
}
void KFilterTest::test_block_read()
{
kDebug() << " -- test_block_read gzip -- ";
test_block_read(pathgz);
#if HAVE_BZIP2_SUPPORT
kDebug() << " -- test_block_read bzip2 -- ";
test_block_read(pathbz2);
#endif
#if HAVE_XZ_SUPPORT
kDebug() << " -- test_block_read lzma -- ";
test_block_read(pathxz);
#endif
}
void KFilterTest::test_getch( const QString & fileName )
{
QIODevice * dev = KFilterDev::deviceForFile( fileName );
QVERIFY( dev != 0 );
bool ok = dev->open( QIODevice::ReadOnly );
QVERIFY( ok );
QByteArray read;
char ch;
while ( dev->getChar(&ch) ) {
//printf("%c",ch);
read += ch;
}
dev->close();
delete dev;
QCOMPARE( read, testData );
}
void KFilterTest::test_getch()
{
kDebug() << " -- test_getch gzip -- ";
test_getch(pathgz);
#if HAVE_BZIP2_SUPPORT
kDebug() << " -- test_getch bzip2 -- ";
test_getch(pathbz2);
#endif
#if HAVE_XZ_SUPPORT
kDebug() << " -- test_getch lzma -- ";
test_getch(pathxz);
#endif
}
void KFilterTest::test_textstream( const QString & fileName )
{
QIODevice * dev = KFilterDev::deviceForFile( fileName );
QVERIFY( dev != 0 );
bool ok = dev->open( QIODevice::ReadOnly );
QVERIFY( ok );
QTextStream ts( dev );
QString readStr = ts.readAll();
dev->close();
delete dev;
QByteArray read = readStr.toLatin1();
QCOMPARE( read, testData );
}
void KFilterTest::test_textstream()
{
kDebug() << " -- test_textstream gzip -- ";
test_textstream(pathgz);
#if HAVE_BZIP2_SUPPORT
kDebug() << " -- test_textstream bzip2 -- ";
test_textstream(pathbz2);
#endif
#if HAVE_XZ_SUPPORT
kDebug() << " -- test_textstream lzma -- ";
test_textstream(pathxz);
#endif
}
void KFilterTest::test_readall(const QString & fileName, const QString& mimeType, const QByteArray& expectedData)
{
QFile file(fileName);
QIODevice *flt = KFilterDev::device(&file, mimeType, false);
QVERIFY(flt);
bool ok = flt->open( QIODevice::ReadOnly );
QVERIFY(ok);
const QByteArray read = flt->readAll();
QCOMPARE(read.size(), expectedData.size());
QCOMPARE(read, expectedData);
delete flt;
}
void KFilterTest::test_readall()
{
kDebug() << " -- test_readall gzip -- ";
test_readall(pathgz, QString::fromLatin1("application/x-gzip"), testData);
#if HAVE_BZIP2_SUPPORT
kDebug() << " -- test_readall bzip2 -- ";
test_readall(pathbz2, QString::fromLatin1("application/x-bzip"), testData);
#endif
#if HAVE_XZ_SUPPORT
kDebug() << " -- test_readall lzma -- ";
test_readall(pathxz, QString::fromLatin1("application/x-xz"), testData);
#endif
kDebug() << " -- test_readall gzip-derived -- ";
test_readall(pathgz, QString::fromLatin1("image/svg+xml-compressed"), testData);
}
void KFilterTest::test_uncompressed()
{
// Can KFilterDev handle uncompressed data even when using gzip decompression?
kDebug() << " -- test_uncompressed -- ";
QBuffer buffer(&testData);
buffer.open(QIODevice::ReadOnly);
QIODevice *flt = KFilterDev::device(&buffer, QString::fromLatin1("application/x-gzip"), false);
bool ok = flt->open( QIODevice::ReadOnly );
QVERIFY(ok);
QByteArray read = flt->readAll();
QCOMPARE( read.size(), testData.size() );
QCOMPARE( read, testData );
delete flt;
}
void KFilterTest::test_findFilterByMimeType_data()
{
QTest::addColumn<QString>("mimeType");
QTest::addColumn<bool>("valid");
// direct mimetype name
QTest::newRow("application/x-gzip") << QString::fromLatin1("application/x-gzip") << true;
#if HAVE_BZIP2_SUPPORT
QTest::newRow("application/x-bzip") << QString::fromLatin1("application/x-bzip") << true;
QTest::newRow("application/x-bzip2") << QString::fromLatin1("application/x-bzip2") << true;
#else
QTest::newRow("application/x-bzip") << QString::fromLatin1("application/x-bzip") << false;
QTest::newRow("application/x-bzip2") << QString::fromLatin1("application/x-bzip2") << false;
#endif
// indirect compressed mimetypes
QTest::newRow("application/x-gzdvi") << QString::fromLatin1("application/x-gzdvi") << true;
// non-compressed mimetypes
QTest::newRow("text/plain") << QString::fromLatin1("text/plain") << false;
QTest::newRow("application/x-tar") << QString::fromLatin1("application/x-tar") << false;
}
void KFilterTest::test_findFilterByMimeType()
{
QFETCH(QString, mimeType);
QFETCH(bool, valid);
KFilterBase *filter = KFilterBase::findFilterByMimeType(mimeType);
QCOMPARE(filter != 0, valid);
delete filter;
}
static void getCompressedData(QByteArray& data, QByteArray& compressedData)
{
data = "Hello world, this is a test for deflate, from bug 114830 / 117683";
compressedData.resize(long(data.size()*1.1f) + 12L); // requirements of zlib::compress2
unsigned long out_bufferlen = compressedData.size();
const int ret = compress2((Bytef*)compressedData.data(), &out_bufferlen, (const Bytef*)data.constData(), data.size(), 1);
QCOMPARE(ret, Z_OK);
compressedData.resize(out_bufferlen);
}
void KFilterTest::test_deflateWithZlibHeader()
{
QByteArray data, deflatedData;
getCompressedData(data, deflatedData);
#if 0 // Can't use KFilterDev for this, we need to call KGzipFilter::init(QIODevice::ReadOnly, KGzipFilter::ZlibHeader);
QBuffer buffer(&deflatedData);
QIODevice *flt = KFilterDev::device(&buffer, "application/x-gzip", false);
static_cast<KFilterDev *>(flt)->setSkipHeaders();
bool ok = flt->open( QIODevice::ReadOnly );
QVERIFY(ok);
const QByteArray read = flt->readAll();
#else
KGzipFilter* mFilterDevice = new KGzipFilter;
mFilterDevice->init(QIODevice::ReadOnly, KGzipFilter::ZlibHeader);
mFilterDevice->setInBuffer(deflatedData.constData(), deflatedData.size());
char buf[8192];
mFilterDevice->setOutBuffer(buf, sizeof(buf));
KFilterBase::Result result = mFilterDevice->uncompress();
QCOMPARE(result, KFilterBase::End);
const int bytesOut = sizeof(buf) - mFilterDevice->outBufferAvailable();
QVERIFY(bytesOut);
QByteArray read(buf, bytesOut);
mFilterDevice->terminate();
delete mFilterDevice;
#endif
QCOMPARE(QString::fromLatin1(read), QString::fromLatin1(data)); // more readable output than the line below
QCOMPARE(read, data);
}
void KFilterTest::test_pushData() // ### UNFINISHED
{
QFile file(pathgz);
QVERIFY(file.open(QIODevice::ReadOnly));
const QByteArray compressed = file.readAll();
const int firstChunkSize = compressed.size() / 2;
QByteArray firstData(compressed, firstChunkSize);
QBuffer inBuffer(&firstData);
QVERIFY(inBuffer.open(QIODevice::ReadWrite));
QIODevice *flt = KFilterDev::device(&inBuffer, "application/x-gzip", false);
QVERIFY(flt->open(QIODevice::ReadOnly));
QByteArray read = flt->readAll();
qDebug() << QString::fromLatin1(read);
// And later...
inBuffer.write(QByteArray(compressed.data() + firstChunkSize, compressed.size() - firstChunkSize));
QCOMPARE(inBuffer.data().size(), compressed.size());
read += flt->readAll();
qDebug() << QString::fromLatin1(read);
delete flt;
}
void KFilterTest::slotFilterOutput(const QByteArray& data)
{
m_filterOutput += data;
}
#include "moc_kfiltertest.cpp"

View file

@ -1,61 +0,0 @@
/*
* Copyright (C) 2002-2005 David Faure <faure@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation;
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KFILTERTEST_H
#define KFILTERTEST_H
#include <QtCore/QObject>
class KFilterTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void test_block_write();
void test_block_read();
void test_biggerWrites();
void test_getch();
void test_textstream();
void test_readall();
void test_uncompressed();
void test_findFilterByMimeType_data();
void test_findFilterByMimeType();
void test_deflateWithZlibHeader();
void test_pushData();
private:
void test_block_write(const QString & fileName, const QByteArray& data);
void test_block_read( const QString & fileName );
void test_getch( const QString & fileName );
void test_textstream( const QString & fileName );
void test_readall(const QString & fileName, const QString& mimeType, const QByteArray& expectedData);
protected Q_SLOTS:
void slotFilterOutput(const QByteArray& data);
private:
QString pathgz;
QString pathbz2;
QString pathxz;
QByteArray testData;
QByteArray m_filterOutput;
};
#endif