7 #include "akonadiprivate_debug.h"
8 #include "compressionstream_p.h"
20 class LZMAErrorCategory :
public std::error_category
23 const char *
name() const noexcept
override
27 std::string
message(
int ev)
const noexcept
override
29 switch (
static_cast<lzma_ret
>(ev)) {
31 return "Operation completed successfully";
33 return "End of stream was reached";
35 return "Input stream has no integrity check";
36 case LZMA_UNSUPPORTED_CHECK:
37 return "Cannot calculate the integrity check";
39 return "Integrity check type is now available";
41 return "Cannot allocate memory";
42 case LZMA_MEMLIMIT_ERROR:
43 return "Memory usage limit was reached";
44 case LZMA_FORMAT_ERROR:
45 return "File format not recognized";
46 case LZMA_OPTIONS_ERROR:
47 return "Invalid or unsupported options";
49 return "Data is corrupt";
51 return "No progress is possible";
53 return "Programming error";
60 const LZMAErrorCategory &lzmaErrorCategory()
62 static const LZMAErrorCategory lzmaErrorCategory{};
63 return lzmaErrorCategory;
70 template<>
struct is_error_code_enum<lzma_ret> : std::true_type {
73 std::error_condition make_error_condition(lzma_ret ret)
75 return std::error_condition(
static_cast<int>(ret), lzmaErrorCategory());
86 std::error_code make_error_code(lzma_ret e)
88 return {
static_cast<int>(e), lzmaErrorCategory()};
91 class Akonadi::Compressor
97 return lzma_auto_decoder(&mStream, 100 * 1024 * 1024 , 0);
99 return lzma_easy_encoder(&mStream, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32);
103 void setInputBuffer(
const char *data, qint64 size)
105 mStream.next_in =
reinterpret_cast<const uint8_t *
>(data);
106 mStream.avail_in = size;
109 void setOutputBuffer(
char *data, qint64 maxSize)
111 mStream.next_out =
reinterpret_cast<uint8_t *
>(data);
112 mStream.avail_out = maxSize;
115 int inputBufferAvailable()
const
117 return mStream.avail_in;
120 int outputBufferAvailable()
const
122 return mStream.avail_out;
125 std::error_code finalize()
131 std::error_code inflate()
133 return lzma_code(&mStream, LZMA_RUN);
136 std::error_code deflate(
bool finish)
138 return lzma_code(&mStream, finish ? LZMA_FINISH : LZMA_RUN);
142 lzma_stream mStream = LZMA_STREAM_INIT;
152 CompressionStream::~CompressionStream()
154 CompressionStream::close();
157 bool CompressionStream::isSequential()
const
162 bool CompressionStream::open(OpenMode mode)
165 qCWarning(AKONADIPRIVATE_LOG) <<
"Invalid open mode for CompressionStream.";
169 mCompressor = std::make_unique<Compressor>();
171 qCWarning(AKONADIPRIVATE_LOG) <<
"Failed to initialize LZMA stream coder:" << err.message();
176 mBuffer.resize(BUFSIZ);
177 mCompressor->setOutputBuffer(mBuffer.data(), mBuffer.size());
183 void CompressionStream::close()
193 mResult = mCompressor->finalize();
198 std::error_code CompressionStream::error()
const
200 return mResult == LZMA_STREAM_END ? LZMA_OK : mResult;
203 bool CompressionStream::atEnd()
const
205 return mResult == LZMA_STREAM_END &&
QIODevice::atEnd() && mStream->atEnd();
208 qint64 CompressionStream::readData(
char *data, qint64 dataSize)
212 if (mResult == LZMA_STREAM_END) {
214 }
else if (mResult != LZMA_OK) {
218 mCompressor->setOutputBuffer(data, dataSize);
220 while (dataSize > 0) {
221 if (mCompressor->inputBufferAvailable() == 0) {
222 mBuffer.resize(BUFSIZ);
223 const auto compressedDataRead = mStream->read(mBuffer.data(), mBuffer.size());
225 if (compressedDataRead > 0) {
226 mCompressor->setInputBuffer(mBuffer.data(), compressedDataRead);
232 mResult = mCompressor->inflate();
234 if (mResult != LZMA_OK && mResult != LZMA_STREAM_END) {
235 qCWarning(AKONADIPRIVATE_LOG) <<
"Error while decompressing LZMA stream:" << mResult.message();
239 const auto decompressedDataRead = dataSize - mCompressor->outputBufferAvailable();
240 dataRead += decompressedDataRead;
241 dataSize -= decompressedDataRead;
243 if (mResult == LZMA_STREAM_END) {
244 if (mStream->atEnd()) {
249 mCompressor->setOutputBuffer(data + dataRead, dataSize);
255 qint64 CompressionStream::writeData(
const char *data, qint64 dataSize)
257 if (mResult != LZMA_OK) {
261 bool finish = (data ==
nullptr);
263 mCompressor->setInputBuffer(data, dataSize);
266 qint64 dataWritten = 0;
268 while (dataSize > 0 || finish) {
269 mResult = mCompressor->deflate(finish);
271 if (mResult != LZMA_OK && mResult != LZMA_STREAM_END) {
272 qCWarning(AKONADIPRIVATE_LOG) <<
"Error while compressing LZMA stream:" << mResult.message();
276 if (mCompressor->inputBufferAvailable() == 0 || (mResult == LZMA_STREAM_END)) {
277 const auto wrote = dataSize - mCompressor->inputBufferAvailable();
279 dataWritten += wrote;
283 mCompressor->setInputBuffer(data + dataWritten, dataSize);
287 if (mCompressor->outputBufferAvailable() == 0 || (mResult == LZMA_STREAM_END) || finish) {
288 const auto toWrite = mBuffer.size() - mCompressor->outputBufferAvailable();
290 const auto writtenSize = mStream->write(mBuffer.constData(), toWrite);
291 if (writtenSize != toWrite) {
292 qCWarning(AKONADIPRIVATE_LOG) <<
"Failed to write compressed data to output device:" << mStream->errorString();
293 setErrorString(QStringLiteral(
"Failed to write compressed data to output device."));
298 if (mResult == LZMA_STREAM_END) {
302 mBuffer.resize(BUFSIZ);
303 mCompressor->setOutputBuffer(mBuffer.data(), mBuffer.size());
310 bool CompressionStream::isCompressed(
QIODevice *data)
312 constexpr std::array<uchar, 6> magic = {0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00};
319 if (data->
peek(buf,
sizeof(buf)) !=
sizeof(buf)) {
323 return memcmp(magic.data(), buf,
sizeof(buf)) == 0;