19 #include "staticcompressed_p.h"
21 #include <Cutelyst/Application>
22 #include <Cutelyst/Request>
23 #include <Cutelyst/Response>
24 #include <Cutelyst/Context>
25 #include <Cutelyst/Engine>
27 #include <QMimeDatabase>
30 #include <QStandardPaths>
31 #include <QCoreApplication>
32 #include <QCryptographicHash>
33 #include <QLoggingCategory>
34 #include <QDataStream>
37 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
41 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
42 #include <brotli/encode.h>
47 Q_LOGGING_CATEGORY(C_STATICCOMPRESSED,
"cutelyst.plugin.staticcompressed", QtWarningMsg)
50 Plugin(parent), d_ptr(new StaticCompressedPrivate)
53 d->includePaths.append(parent->config(QStringLiteral(
"root")).toString());
64 d->includePaths.clear();
65 for (
const QString &path : paths) {
66 d->includePaths.append(QDir(path));
80 const QVariantMap config = app->
engine()->
config(QStringLiteral(
"Cutelyst_StaticCompressed_Plugin"));
81 const QString _defaultCacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String(
"/compressed-static");
82 d->cacheDir.setPath(config.value(QStringLiteral(
"cache_directory"), _defaultCacheDir).toString());
84 if (Q_UNLIKELY(!d->cacheDir.exists())) {
85 if (!d->cacheDir.mkpath(d->cacheDir.absolutePath())) {
86 qCCritical(C_STATICCOMPRESSED,
"Failed to create cache directory for compressed static files at \"%s\".", qPrintable(d->cacheDir.absolutePath()));
91 qCInfo(C_STATICCOMPRESSED,
"Compressed cache directory: %s", qPrintable(d->cacheDir.absolutePath()));
93 const QString _mimeTypes = config.value(QStringLiteral(
"mime_types"), QStringLiteral(
"text/css,application/javascript")).toString();
94 qCInfo(C_STATICCOMPRESSED,
"MIME Types: %s", qPrintable(_mimeTypes));
95 d->mimeTypes = _mimeTypes.split(QLatin1Char(
','), QString::SkipEmptyParts);
97 const QString _suffixes = config.value(QStringLiteral(
"suffixes"), QStringLiteral(
"js.map,css.map,min.js.map,min.css.map")).toString();
98 qCInfo(C_STATICCOMPRESSED,
"Suffixes: %s", qPrintable(_suffixes));
99 d->suffixes = _suffixes.split(QLatin1Char(
','), QString::SkipEmptyParts);
101 d->checkPreCompressed = config.value(QStringLiteral(
"check_pre_compressed"),
true).toBool();
102 qCInfo(C_STATICCOMPRESSED,
"Check for pre-compressed files: %s", d->checkPreCompressed ?
"true" :
"false");
104 d->onTheFlyCompression = config.value(QStringLiteral(
"on_the_fly_compression"),
true).toBool();
105 qCInfo(C_STATICCOMPRESSED,
"Compress static files on the fly: %s", d->onTheFlyCompression ?
"true" :
"false");
107 QStringList supportedCompressions{QStringLiteral(
"deflate"), QStringLiteral(
"gzip")};
110 d->zlibCompressionLevel = config.value(QStringLiteral(
"zlib_compression_level"), 9).toInt(&ok);
111 if (!ok || (d->zlibCompressionLevel < -1) || (d->zlibCompressionLevel > 9)) {
112 d->zlibCompressionLevel = -1;
115 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
116 d->zopfliIterations = config.value(QStringLiteral(
"zopfli_iterations"), 15).toInt(&ok);
117 if (!ok || (d->zopfliIterations < 0)) {
118 d->zopfliIterations = 15;
120 d->useZopfli = config.value(QStringLiteral(
"use_zopfli"),
false).toBool();
121 supportedCompressions << QStringLiteral(
"zopfli");
124 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
125 d->brotliQualityLevel = config.value(QStringLiteral(
"brotli_quality_level"), BROTLI_DEFAULT_QUALITY).toInt(&ok);
126 if (!ok || (d->brotliQualityLevel < BROTLI_MIN_QUALITY) || (d->brotliQualityLevel > BROTLI_MAX_QUALITY)) {
127 d->brotliQualityLevel = BROTLI_DEFAULT_QUALITY;
129 supportedCompressions << QStringLiteral(
"brotli");
132 qCInfo(C_STATICCOMPRESSED,
"Supported compressions: %s", qPrintable(supportedCompressions.join(QLatin1Char(
','))));
135 d->beforePrepareAction(c, skipMethod);
141 void StaticCompressedPrivate::beforePrepareAction(
Context *c,
bool *skipMethod)
147 const QString path = c->req()->path();
148 const QRegularExpression _re = re;
150 for (
const QString &dir : dirs) {
151 if (path.startsWith(dir)) {
152 if (!locateCompressedFile(c, path)) {
156 res->
setBody(QStringLiteral(
"File not found: ") + path);
164 const QRegularExpressionMatch match = _re.match(path);
165 if (match.hasMatch() && locateCompressedFile(c, path)) {
170 bool StaticCompressedPrivate::locateCompressedFile(
Context *c,
const QString &relPath)
const
172 for (
const QDir &includePath : includePaths) {
173 const QString path = includePath.absoluteFilePath(relPath);
174 const QFileInfo fileInfo(path);
175 if (fileInfo.exists()) {
177 const QDateTime currentDateTime = fileInfo.lastModified();
178 if (!c->req()->headers().ifModifiedSince(currentDateTime)) {
183 static QMimeDatabase db;
185 const QMimeType mimeType = db.mimeTypeForFile(path, QMimeDatabase::MatchExtension);
186 QString contentEncoding;
187 QString compressedPath;
188 QString _mimeTypeName;
190 if (mimeType.isValid()) {
194 if (mimeType.isDefault()) {
195 if (path.endsWith(QLatin1String(
"css.map"), Qt::CaseInsensitive) || path.endsWith(QLatin1String(
"js.map"), Qt::CaseInsensitive)) {
196 _mimeTypeName = QStringLiteral(
"application/json");
200 if (mimeTypes.contains(mimeType.name(), Qt::CaseInsensitive) || suffixes.contains(fileInfo.completeSuffix(), Qt::CaseInsensitive)) {
202 const QString acceptEncoding = c->req()->
header(QStringLiteral(
"Accept-Encoding"));
203 qCDebug(C_STATICCOMPRESSED) <<
"Accept-Encoding:" << acceptEncoding;
205 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
206 if (acceptEncoding.contains(QLatin1String(
"br"), Qt::CaseInsensitive)) {
207 compressedPath = locateCacheFile(path, currentDateTime, Brotli) ;
208 if (!compressedPath.isEmpty()) {
209 qCDebug(C_STATICCOMPRESSED,
"Serving brotli compressed data from \"%s\".", qPrintable(compressedPath));
210 contentEncoding = QStringLiteral(
"br");
214 if (acceptEncoding.contains(QLatin1String(
"gzip"), Qt::CaseInsensitive)) {
215 compressedPath = locateCacheFile(path, currentDateTime, useZopfli ? Zopfli : Gzip);
216 if (!compressedPath.isEmpty()) {
217 qCDebug(C_STATICCOMPRESSED,
"Serving %s compressed data from \"%s\".", useZopfli ?
"zopfli" :
"gzip", qPrintable(compressedPath));
218 contentEncoding = QStringLiteral(
"gzip");
220 }
else if (acceptEncoding.contains(QLatin1String(
"deflate"), Qt::CaseInsensitive)) {
221 compressedPath = locateCacheFile(path, currentDateTime, Deflate);
222 if (!compressedPath.isEmpty()) {
223 qCDebug(C_STATICCOMPRESSED,
"Serving deflate compressed data from \"%s\".", qPrintable(compressedPath));
224 contentEncoding = QStringLiteral(
"deflate");
231 QFile *file = !compressedPath.isEmpty() ?
new QFile(compressedPath) : new QFile(path);
232 if (file->open(QFile::ReadOnly)) {
233 qCDebug(C_STATICCOMPRESSED) <<
"Serving" << path;
241 if (!_mimeTypeName.isEmpty()) {
243 }
else if (mimeType.isValid()) {
250 headers.
setHeader(QStringLiteral(
"CACHE_CONTROL"), QStringLiteral(
"public"));
252 if (!contentEncoding.isEmpty()) {
257 headers.
pushHeader(QStringLiteral(
"Vary"), QStringLiteral(
"Accept-Encoding"));
263 qCWarning(C_STATICCOMPRESSED) <<
"Could not serve" << path << file->errorString();
268 qCWarning(C_STATICCOMPRESSED) <<
"File not found" << relPath;
272 QString StaticCompressedPrivate::locateCacheFile(
const QString &origPath,
const QDateTime &origLastModified, Compression compression)
const
274 QString compressedPath;
278 switch (compression) {
281 suffix = QStringLiteral(
".gz");
283 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
285 suffix = QStringLiteral(
".br");
289 suffix = QStringLiteral(
".deflate");
292 Q_ASSERT_X(
false,
"locate cache file",
"invalid compression type");
296 if (checkPreCompressed) {
297 const QFileInfo origCompressed(origPath + suffix);
298 if (origCompressed.exists()) {
299 compressedPath = origCompressed.absoluteFilePath();
300 return compressedPath;
304 if (onTheFlyCompression) {
306 const QString path = cacheDir.absoluteFilePath(QString::fromLatin1(QCryptographicHash::hash(origPath.toUtf8(), QCryptographicHash::Md5).toHex()) + suffix);
307 const QFileInfo info(path);
309 if (info.exists() && (info.lastModified() > origLastModified)) {
310 compressedPath = path;
312 QLockFile lock(path + QLatin1String(
".lock"));
313 if (lock.tryLock(10)) {
314 switch (compression) {
315 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
317 if (compressBrotli(origPath, path)) {
318 compressedPath = path;
323 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
324 if (compressZopfli(origPath, path)) {
325 compressedPath = path;
330 if (compressGzip(origPath, path, origLastModified)) {
331 compressedPath = path;
335 if (compressDeflate(origPath, path)) {
336 compressedPath = path;
347 return compressedPath;
350 static const quint32 crc_32_tab[] = {
351 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
352 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
353 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
354 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
355 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
356 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
357 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
358 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
359 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
360 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
361 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
362 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
363 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
364 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
365 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
366 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
367 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
368 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
369 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
370 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
371 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
372 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
373 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
374 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
375 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
376 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
377 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
378 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
379 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
380 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
381 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
382 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
383 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
384 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
385 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
386 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
387 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
388 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
389 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
390 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
391 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
392 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
393 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
396 quint32 updateCRC32(
unsigned char ch, quint32 crc)
398 return (crc_32_tab[((crc) ^ (quint8(ch))) & 0xff] ^ ((crc) >> 8));
401 quint32 crc32buf(
const QByteArray& data)
403 return ~std::accumulate(
407 [](quint32 oldcrc32,
char buf){
return updateCRC32(buf, oldcrc32); });
410 bool StaticCompressedPrivate::compressGzip(
const QString &inputPath,
const QString &outputPath,
const QDateTime &origLastModified)
const
412 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with gzip to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
414 QFile input(inputPath);
415 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
416 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with gzip:" << inputPath;
420 const QByteArray data = input.readAll();
421 if (Q_UNLIKELY(data.isEmpty())) {
422 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
427 QByteArray compressedData = qCompress(data, zlibCompressionLevel);
430 QFile output(outputPath);
431 if (Q_UNLIKELY(!output.open(QIODevice::WriteOnly))) {
432 qCWarning(C_STATICCOMPRESSED) <<
"Can not open output file to compress with gzip:" << outputPath;
436 if (Q_UNLIKELY(compressedData.isEmpty())) {
437 qCWarning(C_STATICCOMPRESSED) <<
"Failed to compress file with gzip, compressed data is empty:" << inputPath;
438 if (output.exists()) {
439 if (Q_UNLIKELY(!output.remove())) {
440 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed gzip file:" << outputPath;
448 compressedData.remove(0, 6);
449 compressedData.chop(4);
452 QDataStream headerStream(&header, QIODevice::WriteOnly);
454 headerStream << quint16(0x1f8b)
456 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
457 << quint32(origLastModified.toSecsSinceEpoch())
459 << quint32(origLastModified.toTime_t())
461 #if defined Q_OS_UNIX
463 #elif defined Q_OS_WIN
465 #elif defined Q_OS_MACOS
474 QDataStream footerStream(&footer, QIODevice::WriteOnly);
475 footerStream << crc32buf(data)
476 << quint32(data.size());
478 if (Q_UNLIKELY(output.write(header + compressedData + footer) < 0)) {
479 qCCritical(C_STATICCOMPRESSED,
"Failed to write compressed gzip file \"%s\": %s", qPrintable(inputPath), qPrintable(output.errorString()));
486 bool StaticCompressedPrivate::compressDeflate(
const QString &inputPath,
const QString &outputPath)
const
488 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with deflate to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
490 QFile input(inputPath);
491 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
492 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with deflate:" << inputPath;
496 const QByteArray data = input.readAll();
497 if (Q_UNLIKELY(data.isEmpty())) {
498 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
503 QByteArray compressedData = qCompress(data, zlibCompressionLevel);
506 QFile output(outputPath);
507 if (Q_UNLIKELY(!output.open(QIODevice::WriteOnly))) {
508 qCWarning(C_STATICCOMPRESSED) <<
"Can not open output file to compress with deflate:" << outputPath;
512 if (Q_UNLIKELY(compressedData.isEmpty())) {
513 qCWarning(C_STATICCOMPRESSED) <<
"Failed to compress file with deflate, compressed data is empty:" << inputPath;
514 if (output.exists()) {
515 if (Q_UNLIKELY(!output.remove())) {
516 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed deflate file:" << outputPath;
524 compressedData.remove(0, 6);
525 compressedData.chop(4);
527 if (Q_UNLIKELY(output.write(compressedData) < 0)) {
528 qCCritical(C_STATICCOMPRESSED,
"Failed to write compressed deflate file \"%s\": %s", qPrintable(inputPath), qPrintable(output.errorString()));
535 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
536 bool StaticCompressedPrivate::compressZopfli(
const QString &inputPath,
const QString &outputPath)
const
538 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with zopfli to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
540 QFile input(inputPath);
541 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
542 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with zopfli:" << inputPath;
546 const QByteArray data = input.readAll();
547 if (Q_UNLIKELY(data.isEmpty())) {
548 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
553 ZopfliOptions options;
554 ZopfliInitOptions(&options);
555 options.numiterations = zopfliIterations;
557 unsigned char* out = 0;
560 ZopfliCompress(&options, ZopfliFormat::ZOPFLI_FORMAT_GZIP,
reinterpret_cast<const unsigned char *
>(data.constData()), data.size(), &out, &outSize);
564 QFile output(outputPath);
565 if (Q_UNLIKELY(!output.open(QIODevice::WriteOnly))) {
566 qCWarning(C_STATICCOMPRESSED) <<
"Can not open output file to compress with zopfli:" << outputPath;
568 if (Q_UNLIKELY(output.write(
reinterpret_cast<const char *
>(out), outSize) < 0)) {
569 qCCritical(C_STATICCOMPRESSED,
"Failed to write compressed zopfli file \"%s\": %s", qPrintable(inputPath), qPrintable(output.errorString()));
570 if (output.exists()) {
571 if (Q_UNLIKELY(!output.remove())) {
572 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed zopfli file:" << outputPath;
580 qCWarning(C_STATICCOMPRESSED) <<
"Failed to compress file with zopfli, compressed data is empty:" << inputPath;
589 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
590 bool StaticCompressedPrivate::compressBrotli(
const QString &inputPath,
const QString &outputPath)
const
592 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with brotli to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
594 QFile input(inputPath);
595 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
596 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with brotli:" << inputPath;
600 const QByteArray data = input.readAll();
601 if (Q_UNLIKELY(data.isEmpty())) {
602 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
610 size_t outSize = BrotliEncoderMaxCompressedSize(
static_cast<size_t>(data.size()));
611 if (Q_LIKELY(outSize > 0)) {
612 const uint8_t *in = (
const uint8_t *) data.constData();
614 out = (uint8_t *) malloc(
sizeof(uint8_t) * (outSize+1));
615 if (Q_LIKELY(out !=
nullptr)) {
616 BROTLI_BOOL status = BrotliEncoderCompress(brotliQualityLevel, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, data.size(), in, &outSize, out);
617 if (Q_LIKELY(status == BROTLI_TRUE)) {
618 QFile output(outputPath);
619 if (Q_LIKELY(output.open(QIODevice::WriteOnly))) {
620 if (Q_LIKELY(output.write(
reinterpret_cast<const char *
>(out), outSize) > -1)) {
623 qCWarning(C_STATICCOMPRESSED,
"Failed to write brotli compressed data to output file \"%s\": %s", qPrintable(outputPath), qPrintable(output.errorString()));
624 if (output.exists()) {
625 if (Q_UNLIKELY(!output.remove())) {
626 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed brotli file:" << outputPath;
631 qCWarning(C_STATICCOMPRESSED,
"Failed to open output file for brotli compression: %s", qPrintable(outputPath));
634 qCWarning(C_STATICCOMPRESSED,
"Failed to compress \"%s\" with brotli.", qPrintable(inputPath));
638 qCWarning(C_STATICCOMPRESSED,
"Can not allocate needed output buffer of size %lu for brotli compression.",
sizeof(uint8_t) * (outSize+1));
641 qCWarning(C_STATICCOMPRESSED,
"Needed output buffer too large to compress input of size %lu with brotli.",
static_cast<size_t>(data.size()));
648 #include "moc_staticcompressed.cpp"