summaryrefslogtreecommitdiffstats
path: root/src/network/access/qdecompresshelper.cpp
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2020-05-06 18:30:43 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2020-08-14 13:17:11 +0200
commitd40f88e8d23f9d5fdb08ff9a394d9697a0d86fc8 (patch)
tree73384e08f6d362bfc7f1da9f5ce2ff0b28641c4e /src/network/access/qdecompresshelper.cpp
parent94b7a4f9b8d0746dae54b0a1870f119e8b17830d (diff)
QDecompressHelper: Introduce zstd support
Also take this opportunity to reshuffle the content-encodings in the intended ordering since the ordering is used to signify priority. Task-number: QTBUG-83269 Change-Id: I022eecf1ba03b54dbd9c98a9d63d05fb05fd2124 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/network/access/qdecompresshelper.cpp')
-rw-r--r--src/network/access/qdecompresshelper.cpp82
1 files changed, 80 insertions, 2 deletions
diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp
index 4fbb2d8889..25e87bbc43 100644
--- a/src/network/access/qdecompresshelper.cpp
+++ b/src/network/access/qdecompresshelper.cpp
@@ -48,6 +48,10 @@
# include <brotli/decode.h>
#endif
+#if QT_CONFIG(zstd)
+# include <zstd.h>
+#endif
+
#include <array>
QT_BEGIN_NAMESPACE
@@ -59,11 +63,14 @@ struct ContentEncodingMapping
};
constexpr ContentEncodingMapping contentEncodingMapping[] {
- { "gzip", QDecompressHelper::GZip },
- { "deflate", QDecompressHelper::Deflate },
+#if QT_CONFIG(zstd)
+ { "zstd", QDecompressHelper::Zstandard },
+#endif
#if QT_CONFIG(brotli)
{ "br", QDecompressHelper::Brotli },
#endif
+ { "gzip", QDecompressHelper::GZip },
+ { "deflate", QDecompressHelper::Deflate },
};
QDecompressHelper::ContentEncoding encodingFromByteArray(const QByteArray &ce) noexcept
@@ -86,6 +93,13 @@ BrotliDecoderState *toBrotliPointer(void *ptr)
return static_cast<BrotliDecoderState *>(ptr);
}
#endif
+
+#if QT_CONFIG(zstd)
+ZSTD_DStream *toZstandardPointer(void *ptr)
+{
+ return static_cast<ZSTD_DStream *>(ptr);
+}
+#endif
}
bool QDecompressHelper::isSupportedEncoding(const QByteArray &encoding)
@@ -155,6 +169,13 @@ bool QDecompressHelper::setEncoding(ContentEncoding ce)
Q_UNREACHABLE();
#endif
break;
+ case Zstandard:
+#if QT_CONFIG(zstd)
+ decoderPointer = ZSTD_createDStream();
+#else
+ Q_UNREACHABLE();
+#endif
+ break;
}
if (!decoderPointer) {
qWarning("Failed to initialize the decoder.");
@@ -349,6 +370,9 @@ qsizetype QDecompressHelper::read(char *data, qsizetype maxSize)
case Brotli:
bytesRead = readBrotli(data, maxSize);
break;
+ case Zstandard:
+ bytesRead = readZstandard(data, maxSize);
+ break;
}
if (bytesRead == -1)
clear();
@@ -401,6 +425,14 @@ void QDecompressHelper::clear()
#endif
break;
}
+ case Zstandard: {
+#if QT_CONFIG(zstd)
+ ZSTD_DStream *zstdStream = toZstandardPointer(decoderPointer);
+ if (zstdStream)
+ ZSTD_freeDStream(zstdStream);
+#endif
+ break;
+ }
}
decoderPointer = nullptr;
contentEncoding = None;
@@ -604,4 +636,50 @@ qsizetype QDecompressHelper::readBrotli(char *data, const qsizetype maxSize)
#endif
}
+qsizetype QDecompressHelper::readZstandard(char *data, const qsizetype maxSize)
+{
+#if !QT_CONFIG(zstd)
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ Q_UNREACHABLE();
+#else
+ ZSTD_DStream *zstdStream = toZstandardPointer(decoderPointer);
+
+ QByteArray input;
+ if (!compressedDataBuffer.isEmpty())
+ input = compressedDataBuffer.read();
+ ZSTD_inBuffer inBuf { input.constData(), size_t(input.size()), 0 };
+
+ ZSTD_outBuffer outBuf { data, size_t(maxSize), 0 };
+
+ bool dataLeftover = false;
+ qsizetype bytesDecoded = 0;
+ while (outBuf.pos < outBuf.size && (inBuf.pos < inBuf.size || decoderHasData)) {
+
+ dataLeftover = false;
+ size_t retValue = ZSTD_decompressStream(zstdStream, &outBuf, &inBuf);
+ if (ZSTD_isError(retValue)) {
+ qWarning("ZStandard error: %s", ZSTD_getErrorName(retValue));
+ return -1;
+ } else if (retValue >= 0) {
+ decoderHasData = false;
+ bytesDecoded = outBuf.pos;
+ // if pos == size then there may be data left over in internal buffers
+ if (outBuf.pos == outBuf.size) {
+ decoderHasData = true;
+ } else if (inBuf.pos == inBuf.size && !compressedDataBuffer.isEmpty()) {
+ input = compressedDataBuffer.read();
+ inBuf = { input.constData(), size_t(input.size()), 0 };
+ }
+ }
+ }
+ if (inBuf.pos < inBuf.size) {
+ // Some input was left unused; move back to the buffer
+ input = input.mid(QByteArray::size_type(inBuf.pos));
+ compressedDataBuffer.prepend(std::move(input));
+ }
+ return bytesDecoded;
+#endif
+}
+
QT_END_NAMESPACE