Skip to content

Commit

Permalink
RCC: Modernize the compression algorithm selection
Browse files Browse the repository at this point in the history
Instead of using compression level -2 to indicate no compression,
introduce CompressionAlgorithm::None and an equivalent XML attribute.
This commit includes some extra error checking for RCC.

Change-Id: I343f2beed55440a7ac0bfffd1562d64b024463ba
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
  • Loading branch information
thiagomacieira committed Dec 11, 2018
1 parent c18c630 commit 163b5c0
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 24 deletions.
27 changes: 22 additions & 5 deletions src/tools/rcc/main.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
Expand Down Expand Up @@ -127,10 +128,21 @@ int runRcc(int argc, char *argv[])
QCommandLineOption rootOption(QStringLiteral("root"), QStringLiteral("Prefix resource access path with root path."), QStringLiteral("path"));
parser.addOption(rootOption);

#if !defined(QT_NO_COMPRESS)
# define ALGOS "[zlib], none"
#else
# define ALGOS "[none]"
#endif
const QString &algoDescription =
QStringLiteral("Compress input files using algorithm <algo> (" ALGOS ").");
QCommandLineOption compressionAlgoOption(QStringLiteral("compress-algo"), algoDescription, QStringLiteral("algo"));
parser.addOption(compressionAlgoOption);
#undef ALGOS

QCommandLineOption compressOption(QStringLiteral("compress"), QStringLiteral("Compress input files by <level>."), QStringLiteral("level"));
parser.addOption(compressOption);

QCommandLineOption nocompressOption(QStringLiteral("no-compress"), QStringLiteral("Disable all compression."));
QCommandLineOption nocompressOption(QStringLiteral("no-compress"), QStringLiteral("Disable all compression. Same as --compress-algo=none."));
parser.addOption(nocompressOption);

QCommandLineOption thresholdOption(QStringLiteral("threshold"), QStringLiteral("Threshold to consider compressing files."), QStringLiteral("level"));
Expand Down Expand Up @@ -189,10 +201,15 @@ int runRcc(int argc, char *argv[])
|| library.resourceRoot().at(0) != QLatin1Char('/'))
errorMsg = QLatin1String("Root must start with a /");
}
if (parser.isSet(compressOption))
library.setCompressLevel(parser.value(compressOption).toInt());

if (parser.isSet(compressionAlgoOption))
library.setCompressionAlgorithm(RCCResourceLibrary::parseCompressionAlgorithm(parser.value(compressionAlgoOption), &errorMsg));
if (parser.isSet(nocompressOption))
library.setCompressLevel(-2);
library.setCompressionAlgorithm(RCCResourceLibrary::CompressionAlgorithm::None);
if (parser.isSet(compressOption) && errorMsg.isEmpty()) {
int level = library.parseCompressionLevel(library.compressionAlgorithm(), parser.value(compressOption), &errorMsg);
library.setCompressLevel(level);
}
if (parser.isSet(thresholdOption))
library.setCompressThreshold(parser.value(thresholdOption).toInt());
if (parser.isSet(binaryOption))
Expand Down
104 changes: 86 additions & 18 deletions src/tools/rcc/rcc.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
Expand Down Expand Up @@ -51,6 +52,11 @@ enum {
CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70
};

#if !defined(QT_NO_COMPRESS)
# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zlib
#else
# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::None
#endif

#define writeString(s) write(s, sizeof(s))

Expand Down Expand Up @@ -97,6 +103,7 @@ class RCCFileInfo
QLocale::Language language = QLocale::C,
QLocale::Country country = QLocale::AnyCountry,
uint flags = NoFlags,
RCCResourceLibrary::CompressionAlgorithm compressAlgo = CONSTANT_COMPRESSALGO_DEFAULT,
int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT,
int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT);
~RCCFileInfo();
Expand All @@ -115,6 +122,7 @@ class RCCFileInfo
QFileInfo m_fileInfo;
RCCFileInfo *m_parent;
QHash<QString, RCCFileInfo*> m_children;
RCCResourceLibrary::CompressionAlgorithm m_compressAlgo;
int m_compressLevel;
int m_compressThreshold;

Expand All @@ -125,7 +133,7 @@ class RCCFileInfo

RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
QLocale::Language language, QLocale::Country country, uint flags,
int compressLevel, int compressThreshold)
RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel, int compressThreshold)
{
m_name = name;
m_fileInfo = fileInfo;
Expand All @@ -136,6 +144,7 @@ RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
m_nameOffset = 0;
m_dataOffset = 0;
m_childOffset = 0;
m_compressAlgo = compressAlgo;
m_compressLevel = compressLevel;
m_compressThreshold = compressThreshold;
}
Expand Down Expand Up @@ -236,16 +245,26 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
}
QByteArray data = file.readAll();

#ifndef QT_NO_COMPRESS
// Check if compression is useful for this file
if (m_compressLevel != 0 && data.size() != 0) {
QByteArray compressed =
qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);

int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
if (compressRatio >= m_compressThreshold) {
data = compressed;
m_flags |= Compressed;
if (data.size() != 0) {
#ifndef QT_NO_COMPRESS
if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zlib) {
QByteArray compressed =
qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);

int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
if (compressRatio >= m_compressThreshold) {
if (lib.verbose()) {
QString msg = QString::fromLatin1("%1: note: compressed using zlib (%2 -> %3)\n")
.arg(m_name).arg(data.size()).arg(compressed.size());
lib.m_errorDevice->write(msg.toUtf8());
}
data = compressed;
m_flags |= Compressed;
} else if (lib.verbose()) {
QString msg = QString::fromLatin1("%1: note: not compressed\n").arg(m_name);
lib.m_errorDevice->write(msg.toUtf8());
}
}
}
#endif // QT_NO_COMPRESS
Expand Down Expand Up @@ -343,14 +362,16 @@ RCCResourceLibrary::Strings::Strings() :
ATTRIBUTE_PREFIX(QLatin1String("prefix")),
ATTRIBUTE_ALIAS(QLatin1String("alias")),
ATTRIBUTE_THRESHOLD(QLatin1String("threshold")),
ATTRIBUTE_COMPRESS(QLatin1String("compress"))
ATTRIBUTE_COMPRESS(QLatin1String("compress")),
ATTRIBUTE_COMPRESSALGO(QStringLiteral("compression-algorithm"))
{
}

RCCResourceLibrary::RCCResourceLibrary(quint8 formatVersion)
: m_root(0),
m_format(C_Code),
m_verbose(false),
m_compressionAlgo(CONSTANT_COMPRESSALGO_DEFAULT),
m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
m_treeOffset(0),
Expand Down Expand Up @@ -391,6 +412,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
QLocale::Language language = QLocale::c().language();
QLocale::Country country = QLocale::c().country();
QString alias;
auto compressAlgo = m_compressionAlgo;
int compressLevel = m_compressLevel;
int compressThreshold = m_compressThreshold;

Expand Down Expand Up @@ -444,17 +466,27 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();

compressAlgo = m_compressionAlgo;
compressLevel = m_compressLevel;
if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS))
compressLevel = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString().toInt();

compressThreshold = m_compressThreshold;

QString errorString;
if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
compressLevel = parseCompressionLevel(compressAlgo, value, &errorString);
}

// Special case for -no-compress
if (m_compressLevel == -2)
compressAlgo = CompressionAlgorithm::None;

if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();

// Special case for -no-compress. Overrides all other settings.
if (m_compressLevel == -2)
compressLevel = 0;
if (!errorString.isEmpty())
reader.raiseError(errorString);
}
} else {
reader.raiseError(QString(QLatin1String("unexpected tag: %1")).arg(reader.name().toString()));
Expand Down Expand Up @@ -520,6 +552,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
language,
country,
child.isDir() ? RCCFileInfo::Directory : RCCFileInfo::NoFlags,
compressAlgo,
compressLevel,
compressThreshold)
);
Expand All @@ -535,6 +568,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
language,
country,
RCCFileInfo::NoFlags,
compressAlgo,
compressLevel,
compressThreshold)
);
Expand Down Expand Up @@ -729,6 +763,40 @@ RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap(
return rc;
}

RCCResourceLibrary::CompressionAlgorithm RCCResourceLibrary::parseCompressionAlgorithm(QStringView value, QString *errorMsg)
{
if (value == QLatin1String("zlib")) {
#ifdef QT_NO_COMPRESS
*errorMsg = QLatin1String("zlib support not compiled in");
#else
return CompressionAlgorithm::Zlib;
#endif
} else if (value != QLatin1String("none")) {
*errorMsg = QString::fromLatin1("Unknown compression algorithm '%1'").arg(value);
}

return CompressionAlgorithm::None;
}

int RCCResourceLibrary::parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg)
{
bool ok;
int c = level.toInt(&ok);
if (ok) {
switch (algo) {
case CompressionAlgorithm::None:
return 0;
case CompressionAlgorithm::Zlib:
if (c >= 1 && c <= 9)
return c;
break;
}
}

*errorMsg = QString::fromLatin1("invalid compression level '%1'").arg(level);
return 0;
}

bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
{
m_errorDevice = &errorDevice;
Expand Down
16 changes: 15 additions & 1 deletion src/tools/rcc/rcc.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
Expand Down Expand Up @@ -77,6 +78,17 @@ class RCCResourceLibrary
void setOutputName(const QString &name) { m_outputName = name; }
QString outputName() const { return m_outputName; }

enum class CompressionAlgorithm {
Zlib,

None = -1
};

static CompressionAlgorithm parseCompressionAlgorithm(QStringView algo, QString *errorMsg);
void setCompressionAlgorithm(CompressionAlgorithm algo) { m_compressionAlgo = algo; }
CompressionAlgorithm compressionAlgorithm() const { return m_compressionAlgo; }

static int parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg);
void setCompressLevel(int c) { m_compressLevel = c; }
int compressLevel() const { return m_compressLevel; }

Expand Down Expand Up @@ -104,6 +116,7 @@ class RCCResourceLibrary
const QString ATTRIBUTE_ALIAS;
const QString ATTRIBUTE_THRESHOLD;
const QString ATTRIBUTE_COMPRESS;
const QString ATTRIBUTE_COMPRESSALGO;
};
friend class RCCFileInfo;
void reset();
Expand Down Expand Up @@ -133,6 +146,7 @@ class RCCResourceLibrary
QString m_outputName;
Format m_format;
bool m_verbose;
CompressionAlgorithm m_compressionAlgo;
int m_compressLevel;
int m_compressThreshold;
int m_treeOffset;
Expand Down

0 comments on commit 163b5c0

Please sign in to comment.