Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/compress_helpers.cpp
4212 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "compress_helpers.h"
5
6
#include "common/align.h"
7
#include "common/assert.h"
8
#include "common/error.h"
9
#include "common/file_system.h"
10
#include "common/log.h"
11
#include "common/path.h"
12
#include "common/scoped_guard.h"
13
#include "common/string_util.h"
14
15
#include "7zCrc.h"
16
#include "Alloc.h"
17
#include "Xz.h"
18
#include "XzCrc64.h"
19
#include "XzEnc.h"
20
21
#include <zlib.h>
22
#include <zstd.h>
23
#include <zstd_errors.h>
24
25
LOG_CHANNEL(CompressHelpers);
26
27
// TODO: Use streaming API to avoid mallocing the whole input buffer. But one read() call is probably still faster..
28
29
namespace CompressHelpers {
30
static std::optional<CompressType> GetCompressType(const std::string_view path, Error* error);
31
32
static const char* ZlibErrorToString(int res);
33
static std::optional<size_t> GetDeflateDecompressedSize(std::span<const u8> data, Error* error);
34
static bool DecompressDeflate(std::span<u8> dst, size_t uncompressed_size, std::span<const u8> data, Error* error);
35
36
static std::optional<size_t> GetZstdDecompressedSize(std::span<const u8> data, Error* error);
37
static bool DecompressZstd(std::span<u8> dst, size_t uncompressed_size, std::span<const u8> data, Error* error);
38
39
template<typename T>
40
static bool DecompressHelper(ByteBuffer& ret, CompressType type, T data, std::optional<size_t> decompressed_size,
41
Error* error);
42
43
template<typename T>
44
static bool CompressHelper(ByteBuffer& ret, CompressType type, T data, int clevel, Error* error);
45
46
static void Init7ZCRCTables();
47
static bool XzCompress(ByteBuffer& ret, const u8* data, size_t data_size, int clevel, Error* error);
48
49
static std::once_flag s_lzma_crc_table_init;
50
51
} // namespace CompressHelpers
52
53
std::optional<CompressHelpers::CompressType> CompressHelpers::GetCompressType(const std::string_view path, Error* error)
54
{
55
const std::string_view extension = Path::GetExtension(path);
56
if (StringUtil::EqualNoCase(extension, "zst"))
57
return CompressType::Zstandard;
58
else if (StringUtil::EqualNoCase(extension, "xz"))
59
return CompressType::XZ;
60
61
return CompressType::Uncompressed;
62
}
63
64
const char* CompressHelpers::ZlibErrorToString(int res)
65
{
66
// clang-format off
67
switch (res)
68
{
69
case Z_OK: return "Z_OK";
70
case Z_STREAM_END: return "Z_STREAM_END";
71
case Z_NEED_DICT: return "Z_NEED_DICT";
72
case Z_ERRNO: return "Z_ERRNO";
73
case Z_STREAM_ERROR: return "Z_STREAM_ERROR";
74
case Z_DATA_ERROR: return "Z_DATA_ERROR";
75
case Z_MEM_ERROR: return "Z_MEM_ERROR";
76
case Z_BUF_ERROR: return "Z_BUF_ERROR";
77
case Z_VERSION_ERROR: return "Z_VERSION_ERROR";
78
default: return "Z_UNKNOWN_ERROR";
79
}
80
// clang-format on
81
}
82
83
std::optional<size_t> CompressHelpers::GetDeflateDecompressedSize(std::span<const u8> data, Error* error)
84
{
85
z_stream zs;
86
int res = inflateInit(&zs);
87
if (res != Z_OK)
88
{
89
Error::SetStringFmt(error, "inflateInit() failed: {} ({})", ZlibErrorToString(res), res);
90
return false;
91
}
92
93
u8 temp_buf[1024];
94
95
zs.next_in = const_cast<u8*>(data.data());
96
zs.avail_in = static_cast<u32>(data.size());
97
98
while (zs.avail_in > 0)
99
{
100
zs.next_out = temp_buf;
101
zs.avail_out = sizeof(temp_buf);
102
103
res = inflate(&zs, Z_FINISH);
104
if (res != Z_OK)
105
{
106
Error::SetStringFmt(error, "inflate() failed: {} ({})", ZlibErrorToString(res), res);
107
inflateEnd(&zs);
108
return std::nullopt;
109
}
110
}
111
112
inflateEnd(&zs);
113
return zs.total_out;
114
}
115
116
bool CompressHelpers::DecompressDeflate(std::span<u8> dst, size_t uncompressed_size, std::span<const u8> data,
117
Error* error)
118
{
119
unsigned long src_len = static_cast<unsigned long>(data.size());
120
unsigned long dest_len = static_cast<unsigned long>(uncompressed_size);
121
const int res = uncompress2(dst.data(), &dest_len, data.data(), &src_len);
122
if (res != Z_OK)
123
{
124
Error::SetStringFmt(error, "uncompress2() failed: {} ({})", ZlibErrorToString(res), res);
125
return false;
126
}
127
128
return true;
129
}
130
131
std::optional<size_t> CompressHelpers::GetZstdDecompressedSize(std::span<const u8> data, Error* error)
132
{
133
const unsigned long long runtime_decompressed_size = ZSTD_getFrameContentSize(data.data(), data.size());
134
if (runtime_decompressed_size == ZSTD_CONTENTSIZE_UNKNOWN || runtime_decompressed_size == ZSTD_CONTENTSIZE_ERROR ||
135
runtime_decompressed_size >= std::numeric_limits<size_t>::max()) [[unlikely]]
136
{
137
Error::SetStringView(error, "Failed to get uncompressed size.");
138
return false;
139
}
140
141
return static_cast<size_t>(runtime_decompressed_size);
142
}
143
144
bool CompressHelpers::DecompressZstd(std::span<u8> dst, size_t uncompressed_size, std::span<const u8> data,
145
Error* error)
146
{
147
if (dst.size() < uncompressed_size)
148
{
149
Error::SetStringFmt(error, "Destination buffer is too small, expected {}, got {}", uncompressed_size, dst.size());
150
return false;
151
}
152
153
const size_t result = ZSTD_decompress(dst.data(), dst.size(), data.data(), data.size());
154
if (ZSTD_isError(result)) [[unlikely]]
155
{
156
const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(result));
157
Error::SetStringFmt(error, "ZSTD_decompress() failed: {}", errstr ? errstr : "<unknown>");
158
return false;
159
}
160
else if (result != uncompressed_size) [[unlikely]]
161
{
162
Error::SetStringFmt(error, "ZSTD_decompress() only returned {} of {} bytes.", result, uncompressed_size);
163
return false;
164
}
165
166
return true;
167
}
168
169
void CompressHelpers::Init7ZCRCTables()
170
{
171
std::call_once(s_lzma_crc_table_init, []() {
172
CrcGenerateTable();
173
Crc64GenerateTable();
174
});
175
}
176
177
const char* CompressHelpers::SZErrorToString(int res)
178
{
179
// clang-format off
180
switch (res)
181
{
182
case SZ_OK: return "SZ_OK";
183
case SZ_ERROR_DATA: return "SZ_ERROR_DATA";
184
case SZ_ERROR_MEM: return "SZ_ERROR_MEM";
185
case SZ_ERROR_CRC: return "SZ_ERROR_CRC";
186
case SZ_ERROR_UNSUPPORTED: return "SZ_ERROR_UNSUPPORTED";
187
case SZ_ERROR_PARAM: return "SZ_ERROR_PARAM";
188
case SZ_ERROR_INPUT_EOF: return "SZ_ERROR_INPUT_EOF";
189
case SZ_ERROR_OUTPUT_EOF: return "SZ_ERROR_OUTPUT_EOF";
190
case SZ_ERROR_READ: return "SZ_ERROR_READ";
191
case SZ_ERROR_WRITE: return "SZ_ERROR_WRITE";
192
case SZ_ERROR_PROGRESS: return "SZ_ERROR_PROGRESS";
193
case SZ_ERROR_FAIL: return "SZ_ERROR_FAIL";
194
case SZ_ERROR_THREAD: return "SZ_ERROR_THREAD";
195
case SZ_ERROR_ARCHIVE: return "SZ_ERROR_ARCHIVE";
196
case SZ_ERROR_NO_ARCHIVE: return "SZ_ERROR_NO_ARCHIVE";
197
default: return "SZ_UNKNOWN";
198
}
199
// clang-format on
200
}
201
202
namespace CompressHelpers {
203
204
namespace {
205
class XzDecompressor
206
{
207
public:
208
XzDecompressor();
209
~XzDecompressor();
210
211
ALWAYS_INLINE size_t GetStreamSize() const { return stream_size; }
212
213
bool Init(std::span<const u8> data, Error* error);
214
215
bool Decompress(std::span<u8> dst, Error* error);
216
217
private:
218
static constexpr size_t kInputBufSize = static_cast<size_t>(1) << 18;
219
220
struct MyInStream
221
{
222
ISeekInStream vt;
223
const u8* data;
224
size_t data_size;
225
size_t data_pos;
226
};
227
228
CLookToRead2 look_stream = {};
229
MyInStream mis = {};
230
CXzs xzs = {};
231
size_t stream_size = 0;
232
};
233
} // namespace
234
} // namespace CompressHelpers
235
236
CompressHelpers::XzDecompressor::XzDecompressor()
237
{
238
Xzs_Construct(&xzs);
239
}
240
241
CompressHelpers::XzDecompressor::~XzDecompressor()
242
{
243
Xzs_Free(&xzs, &g_Alloc);
244
245
if (look_stream.buf)
246
ISzAlloc_Free(&g_Alloc, look_stream.buf);
247
}
248
249
bool CompressHelpers::XzDecompressor::Init(std::span<const u8> data, Error* error)
250
{
251
Init7ZCRCTables();
252
253
LookToRead2_INIT(&look_stream);
254
LookToRead2_CreateVTable(&look_stream, False);
255
look_stream.realStream = &mis.vt;
256
look_stream.bufSize = kInputBufSize;
257
look_stream.buf = static_cast<Byte*>(ISzAlloc_Alloc(&g_Alloc, kInputBufSize));
258
if (!look_stream.buf)
259
{
260
Error::SetString(error, "Failed to allocate lookahead buffer");
261
return false;
262
}
263
264
mis = {.vt = {.Read = [](const ISeekInStream* p, void* buf, size_t* size) -> SRes {
265
MyInStream* mis = Z7_CONTAINER_FROM_VTBL(p, MyInStream, vt);
266
const size_t size_to_read = *size;
267
const size_t size_to_copy = std::min(size_to_read, mis->data_size - mis->data_pos);
268
std::memcpy(buf, &mis->data[mis->data_pos], size_to_copy);
269
mis->data_pos += size_to_copy;
270
return (size_to_copy == size_to_read) ? SZ_OK : SZ_ERROR_READ;
271
},
272
.Seek = [](const ISeekInStream* p, Int64* pos, ESzSeek origin) -> SRes {
273
MyInStream* mis = Z7_CONTAINER_FROM_VTBL(p, MyInStream, vt);
274
static_assert(SZ_SEEK_CUR == SEEK_CUR && SZ_SEEK_SET == SEEK_SET && SZ_SEEK_END == SEEK_END);
275
if (origin == SZ_SEEK_SET)
276
{
277
if (*pos < 0 || static_cast<size_t>(*pos) > mis->data_size)
278
return SZ_ERROR_READ;
279
mis->data_pos = static_cast<size_t>(*pos);
280
return SZ_OK;
281
}
282
else if (origin == SZ_SEEK_END)
283
{
284
mis->data_pos = mis->data_size;
285
*pos = static_cast<s64>(mis->data_pos);
286
return SZ_OK;
287
}
288
else if (origin == SZ_SEEK_CUR)
289
{
290
const s64 new_pos = static_cast<s64>(mis->data_pos) + *pos;
291
if (new_pos < 0 || static_cast<size_t>(new_pos) > mis->data_size)
292
return SZ_ERROR_READ;
293
mis->data_pos = static_cast<size_t>(new_pos);
294
*pos = new_pos;
295
return SZ_OK;
296
}
297
else
298
{
299
return SZ_ERROR_READ;
300
}
301
}},
302
.data = data.data(),
303
.data_size = data.size(),
304
.data_pos = 0};
305
306
// Read blocks
307
Int64 start_pos = static_cast<Int64>(mis.data_size);
308
SRes res = Xzs_ReadBackward(&xzs, &look_stream.vt, &start_pos, nullptr, &g_Alloc);
309
if (res != SZ_OK)
310
{
311
Error::SetStringFmt(error, "Xzs_ReadBackward() failed: {} ({})", SZErrorToString(res), res);
312
return false;
313
}
314
315
const size_t num_blocks = Xzs_GetNumBlocks(&xzs);
316
if (num_blocks == 0)
317
{
318
Error::SetString(error, "Stream has no blocks.");
319
return false;
320
}
321
322
// compute output size
323
for (int sn = static_cast<int>(xzs.num - 1); sn >= 0; sn--)
324
{
325
const CXzStream& stream = xzs.streams[sn];
326
for (size_t bn = 0; bn < stream.numBlocks; bn++)
327
{
328
const CXzBlockSizes& block = stream.blocks[bn];
329
stream_size += block.unpackSize;
330
}
331
}
332
333
return true;
334
}
335
336
bool CompressHelpers::XzDecompressor::Decompress(std::span<u8> dst, Error* error)
337
{
338
if (dst.size() < stream_size)
339
{
340
Error::SetStringFmt(error, "Destination buffer is too small, expected {}, got {}", stream_size, dst.size());
341
return false;
342
}
343
344
size_t out_pos = 0;
345
346
CXzUnpacker unpacker = {};
347
XzUnpacker_Construct(&unpacker, &g_Alloc);
348
349
for (int sn = static_cast<int>(xzs.num - 1); sn >= 0; sn--)
350
{
351
const CXzStream& stream = xzs.streams[sn];
352
size_t src_offset = stream.startOffset + XZ_STREAM_HEADER_SIZE;
353
if (src_offset >= mis.data_size)
354
break;
355
356
for (size_t bn = 0; bn < stream.numBlocks; bn++)
357
{
358
const CXzBlockSizes& block = stream.blocks[bn];
359
360
XzUnpacker_Init(&unpacker);
361
unpacker.streamFlags = stream.flags;
362
XzUnpacker_PrepareToRandomBlockDecoding(&unpacker);
363
XzUnpacker_SetOutBuf(&unpacker, &dst[out_pos], dst.size() - out_pos);
364
365
const size_t orig_compressed_size =
366
std::min<size_t>(Common::AlignUpPow2(block.totalSize, 4),
367
static_cast<size_t>(mis.data_size - src_offset)); // LZMA blocks are 4 byte aligned?;
368
369
SizeT block_uncompressed_size = block.unpackSize;
370
SizeT block_compressed_size = orig_compressed_size;
371
372
ECoderStatus status;
373
SRes res = XzUnpacker_Code(&unpacker, nullptr, &block_uncompressed_size, &mis.data[src_offset],
374
&block_compressed_size, true, CODER_FINISH_END, &status);
375
if (res != SZ_OK || status != CODER_STATUS_FINISHED_WITH_MARK) [[unlikely]]
376
{
377
Error::SetStringFmt(error, "XzUnpacker_Code() failed: {} ({}) (status {})", SZErrorToString(res), res,
378
static_cast<unsigned>(status));
379
return false;
380
}
381
382
if (block_compressed_size != orig_compressed_size || block_uncompressed_size != block.unpackSize)
383
{
384
WARNING_LOG("Decompress size mismatch: {}/{} vs {}/{}", block_compressed_size, block_uncompressed_size,
385
orig_compressed_size, block.unpackSize);
386
}
387
388
out_pos += block_uncompressed_size;
389
src_offset += block_compressed_size;
390
}
391
}
392
393
if (out_pos != stream_size)
394
{
395
Error::SetStringFmt(error, "Only decompressed {} of {} bytes", out_pos, stream_size);
396
return false;
397
}
398
399
return true;
400
}
401
402
bool CompressHelpers::XzCompress(ByteBuffer& ret, const u8* data, size_t data_size, int clevel, Error* error)
403
{
404
Init7ZCRCTables();
405
406
struct MemoryInStream
407
{
408
ISeqInStream vt;
409
const u8* buffer;
410
size_t buffer_size;
411
size_t read_pos;
412
};
413
MemoryInStream mis = {{.Read = [](const ISeqInStream* p, void* buf, size_t* size) -> SRes {
414
MemoryInStream* mis = Z7_CONTAINER_FROM_VTBL(p, MemoryInStream, vt);
415
const size_t avail = mis->buffer_size - mis->read_pos;
416
const size_t copy = std::min(avail, *size);
417
418
std::memcpy(buf, &mis->buffer[mis->read_pos], copy);
419
mis->read_pos += copy;
420
*size = copy;
421
return SZ_OK;
422
}},
423
data,
424
data_size,
425
0};
426
427
if (ret.empty())
428
ret.resize(data_size / 2);
429
430
// Bit crap, extra copy here..
431
struct DumpOutStream
432
{
433
ISeqOutStream vt;
434
DynamicHeapArray<u8>* out_data;
435
size_t out_pos;
436
};
437
DumpOutStream dos = {.vt = {.Write = [](const ISeqOutStream* p, const void* buf, size_t size) -> size_t {
438
DumpOutStream* dos = Z7_CONTAINER_FROM_VTBL(p, DumpOutStream, vt);
439
if ((dos->out_pos + size) > dos->out_data->size())
440
dos->out_data->resize(std::max(dos->out_pos + size, dos->out_data->size() * 2));
441
std::memcpy(dos->out_data->data() + dos->out_pos, buf, size);
442
dos->out_pos += size;
443
return size;
444
}},
445
.out_data = &ret,
446
.out_pos = 0};
447
448
CXzProps props;
449
XzProps_Init(&props);
450
props.lzma2Props.lzmaProps.level = std::clamp(clevel, 1, 9);
451
452
const SRes res = Xz_Encode(&dos.vt, &mis.vt, &props, nullptr);
453
if (res != SZ_OK)
454
{
455
Error::SetStringFmt(error, "Xz_Encode() failed: {} ({})", SZErrorToString(res), static_cast<int>(res));
456
return false;
457
}
458
459
ret.resize(dos.out_pos);
460
return true;
461
}
462
463
std::optional<size_t> CompressHelpers::GetDecompressedSize(CompressType type, std::span<const u8> data,
464
Error* error /*= nullptr*/)
465
{
466
if (data.size() == 0) [[unlikely]]
467
{
468
Error::SetStringView(error, "Buffer is empty.");
469
return false;
470
}
471
472
switch (type)
473
{
474
case CompressType::Uncompressed:
475
{
476
return data.size();
477
}
478
479
case CompressType::Deflate:
480
{
481
return GetDeflateDecompressedSize(data, error);
482
}
483
484
case CompressType::Zstandard:
485
{
486
return GetZstdDecompressedSize(data, error);
487
}
488
489
case CompressType::XZ:
490
{
491
XzDecompressor dc;
492
if (!dc.Init(data, error))
493
return std::nullopt;
494
495
return dc.GetStreamSize();
496
}
497
498
DefaultCaseIsUnreachable()
499
}
500
}
501
502
template<typename T>
503
bool CompressHelpers::DecompressHelper(ByteBuffer& ret, CompressType type, T data,
504
std::optional<size_t> decompressed_size, Error* error)
505
{
506
if (data.size() == 0) [[unlikely]]
507
{
508
Error::SetStringView(error, "Buffer is empty.");
509
return false;
510
}
511
512
switch (type)
513
{
514
case CompressType::Uncompressed:
515
{
516
if constexpr (std::is_same_v<T, ByteBuffer>)
517
ret = std::move(data);
518
else
519
ret = ByteBuffer(std::move(data));
520
return true;
521
}
522
523
case CompressType::Deflate:
524
{
525
if (!decompressed_size.has_value() && !(decompressed_size = GetZstdDecompressedSize(data, error)).has_value())
526
return false;
527
528
ret.resize(decompressed_size.value());
529
return DecompressDeflate(ret.span(), decompressed_size.value(), data, error);
530
}
531
532
case CompressType::Zstandard:
533
{
534
if (!decompressed_size.has_value() && !(decompressed_size = GetZstdDecompressedSize(data, error)).has_value())
535
return false;
536
537
ret.resize(decompressed_size.value());
538
return DecompressZstd(ret.span(), decompressed_size.value(), data, error);
539
}
540
541
case CompressType::XZ:
542
{
543
XzDecompressor dc;
544
if (!dc.Init(data, error))
545
return false;
546
547
ret.resize(dc.GetStreamSize());
548
return dc.Decompress(ret.span(), error);
549
}
550
551
DefaultCaseIsUnreachable()
552
}
553
}
554
555
std::optional<size_t> CompressHelpers::DecompressBuffer(std::span<u8> dst, CompressType type, std::span<const u8> data,
556
std::optional<size_t> decompressed_size /*= std::nullopt*/,
557
Error* error /*= nullptr*/)
558
{
559
if (data.size() == 0) [[unlikely]]
560
{
561
Error::SetStringView(error, "Buffer is empty.");
562
return std::nullopt;
563
}
564
565
switch (type)
566
{
567
case CompressType::Uncompressed:
568
{
569
if (dst.size() < data.size())
570
{
571
Error::SetStringFmt(error, "Destination buffer is too small, expected {}, got {}", data.size(), dst.size());
572
return std::nullopt;
573
}
574
575
std::memcpy(dst.data(), data.data(), data.size());
576
return data.size();
577
}
578
579
case CompressType::Deflate:
580
{
581
if (!decompressed_size.has_value() && !(decompressed_size = GetZstdDecompressedSize(data, error)).has_value())
582
return std::nullopt;
583
584
return DecompressDeflate(dst, decompressed_size.value(), data, error);
585
}
586
587
case CompressType::Zstandard:
588
{
589
if (!decompressed_size.has_value() && !(decompressed_size = GetZstdDecompressedSize(data, error)).has_value())
590
return std::nullopt;
591
592
if (!DecompressZstd(dst, decompressed_size.value(), data, error))
593
return std::nullopt;
594
595
return decompressed_size;
596
}
597
598
case CompressType::XZ:
599
{
600
XzDecompressor dc;
601
if (!dc.Init(data, error))
602
return false;
603
604
if (!dc.Decompress(dst, error))
605
return std::nullopt;
606
607
return decompressed_size;
608
}
609
610
DefaultCaseIsUnreachable()
611
}
612
}
613
614
template<typename T>
615
bool CompressHelpers::CompressHelper(ByteBuffer& ret, CompressType type, T data, int clevel, Error* error)
616
{
617
if (data.size() == 0) [[unlikely]]
618
{
619
Error::SetStringView(error, "Buffer is empty.");
620
return false;
621
}
622
623
switch (type)
624
{
625
case CompressType::Uncompressed:
626
{
627
if constexpr (std::is_same_v<T, ByteBuffer>)
628
ret = std::move(data);
629
else
630
ret = ByteBuffer(std::move(data));
631
return true;
632
}
633
634
case CompressType::Deflate:
635
{
636
unsigned long compressed_size = compressBound(static_cast<uLong>(data.size()));
637
if (compressed_size == 0) [[unlikely]]
638
{
639
Error::SetStringView(error, "ZSTD_compressBound() failed.");
640
return false;
641
}
642
643
ret.resize(compressed_size);
644
645
const int err = compress2(ret.data(), &compressed_size, data.data(), static_cast<uLong>(data.size()), clevel);
646
if (err != Z_OK) [[unlikely]]
647
{
648
Error::SetStringFmt(error, "compress2() failed: {} ({})", ZlibErrorToString(err), err);
649
return false;
650
}
651
652
ret.resize(compressed_size);
653
return true;
654
}
655
656
case CompressType::Zstandard:
657
{
658
const size_t compressed_size = ZSTD_compressBound(data.size());
659
if (compressed_size == 0) [[unlikely]]
660
{
661
Error::SetStringView(error, "ZSTD_compressBound() failed.");
662
return false;
663
}
664
665
ret.resize(compressed_size);
666
667
const size_t result = ZSTD_compress(ret.data(), compressed_size, data.data(), data.size(),
668
(clevel < 0) ? 0 : std::clamp(clevel, 1, 22));
669
if (ZSTD_isError(result)) [[unlikely]]
670
{
671
const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(result));
672
Error::SetStringFmt(error, "ZSTD_compress() failed: {}", errstr ? errstr : "<unknown>");
673
return false;
674
}
675
676
ret.resize(result);
677
return true;
678
}
679
680
case CompressType::XZ:
681
{
682
return XzCompress(ret, data.data(), data.size(), clevel, error);
683
}
684
685
DefaultCaseIsUnreachable()
686
}
687
}
688
689
CompressHelpers::OptionalByteBuffer CompressHelpers::DecompressBuffer(CompressType type, std::span<const u8> data,
690
std::optional<size_t> decompressed_size,
691
Error* error)
692
{
693
CompressHelpers::OptionalByteBuffer ret = ByteBuffer();
694
if (!DecompressHelper(ret.value(), type, data, decompressed_size, error))
695
ret.reset();
696
return ret;
697
}
698
699
CompressHelpers::OptionalByteBuffer CompressHelpers::DecompressBuffer(CompressType type, OptionalByteBuffer data,
700
std::optional<size_t> decompressed_size,
701
Error* error)
702
{
703
OptionalByteBuffer ret;
704
if (data.has_value())
705
{
706
ret = ByteBuffer();
707
if (!DecompressHelper(ret.value(), type, std::move(data.value()), decompressed_size, error))
708
ret.reset();
709
}
710
else
711
{
712
if (error && !error->IsValid())
713
error->SetStringView("Data buffer is empty.");
714
}
715
716
return ret;
717
}
718
719
bool CompressHelpers::DecompressBuffer(ByteBuffer& dst, CompressType type, std::span<const u8> data,
720
std::optional<size_t> decompressed_size /*= std::nullopt*/,
721
Error* error /*= nullptr*/)
722
{
723
return DecompressHelper(dst, type, data, decompressed_size, error);
724
}
725
726
CompressHelpers::OptionalByteBuffer CompressHelpers::DecompressFile(std::string_view path, std::span<const u8> data,
727
std::optional<size_t> decompressed_size,
728
Error* error)
729
{
730
OptionalByteBuffer ret;
731
const std::optional<CompressType> type = GetCompressType(path, error);
732
if (type.has_value())
733
ret = DecompressBuffer(type.value(), data, decompressed_size, error);
734
return ret;
735
}
736
737
CompressHelpers::OptionalByteBuffer CompressHelpers::DecompressFile(std::string_view path, OptionalByteBuffer data,
738
std::optional<size_t> decompressed_size,
739
Error* error)
740
{
741
OptionalByteBuffer ret;
742
const std::optional<CompressType> type = GetCompressType(path, error);
743
if (type.has_value())
744
ret = DecompressBuffer(type.value(), std::move(data), decompressed_size, error);
745
return ret;
746
}
747
748
CompressHelpers::OptionalByteBuffer
749
CompressHelpers::DecompressFile(const char* path, std::optional<size_t> decompressed_size, Error* error)
750
{
751
OptionalByteBuffer ret;
752
const std::optional<CompressType> type = GetCompressType(path, error);
753
if (type.has_value())
754
ret = DecompressFile(type.value(), path, decompressed_size, error);
755
return ret;
756
}
757
758
CompressHelpers::OptionalByteBuffer CompressHelpers::DecompressFile(CompressType type, const char* path,
759
std::optional<size_t> decompressed_size,
760
Error* error)
761
{
762
OptionalByteBuffer ret;
763
OptionalByteBuffer data = FileSystem::ReadBinaryFile(path, error);
764
if (data.has_value())
765
ret = DecompressBuffer(type, std::move(data), decompressed_size, error);
766
return ret;
767
}
768
769
CompressHelpers::OptionalByteBuffer CompressHelpers::CompressToBuffer(CompressType type, std::span<const u8> data,
770
int clevel, Error* error)
771
{
772
OptionalByteBuffer ret = ByteBuffer();
773
if (!CompressHelper(ret.value(), type, data, clevel, error))
774
ret.reset();
775
return ret;
776
}
777
778
CompressHelpers::OptionalByteBuffer CompressHelpers::CompressToBuffer(CompressType type, const void* data,
779
size_t data_size, int clevel, Error* error)
780
{
781
OptionalByteBuffer ret = ByteBuffer();
782
if (!CompressHelper(ret.value(), type, std::span<const u8>(static_cast<const u8*>(data), data_size), clevel, error))
783
ret.reset();
784
return ret;
785
}
786
787
CompressHelpers::OptionalByteBuffer CompressHelpers::CompressToBuffer(CompressType type, OptionalByteBuffer data,
788
int clevel, Error* error)
789
{
790
OptionalByteBuffer ret = ByteBuffer();
791
if (!CompressHelper(ret.value(), type, std::move(data.value()), clevel, error))
792
ret.reset();
793
return ret;
794
}
795
796
bool CompressHelpers::CompressToBuffer(ByteBuffer& dst, CompressType type, std::span<const u8> data,
797
int clevel /*= -1*/, Error* error /*= nullptr*/)
798
{
799
return CompressHelper(dst, type, data, clevel, error);
800
}
801
802
bool CompressHelpers::CompressToBuffer(ByteBuffer& dst, CompressType type, ByteBuffer data, int clevel /*= -1*/,
803
Error* error /*= nullptr*/)
804
{
805
return CompressHelper(dst, type, std::move(data), clevel, error);
806
}
807
808
bool CompressHelpers::CompressToFile(const char* path, std::span<const u8> data, int clevel, bool atomic_write,
809
Error* error)
810
{
811
const std::optional<CompressType> type = GetCompressType(path, error);
812
if (!type.has_value())
813
return false;
814
815
return CompressToFile(type.value(), path, data, clevel, atomic_write, error);
816
}
817
818
bool CompressHelpers::CompressToFile(CompressType type, const char* path, std::span<const u8> data, int clevel,
819
bool atomic_write, Error* error)
820
{
821
const OptionalByteBuffer cdata = CompressToBuffer(type, data, clevel, error);
822
if (!cdata.has_value())
823
return false;
824
825
return atomic_write ? FileSystem::WriteAtomicRenamedFile(path, cdata->data(), cdata->size(), error) :
826
FileSystem::WriteBinaryFile(path, cdata->data(), cdata->size(), error);
827
}
828
829