9#if !__has_include(<zstd.h>)
10#error "Missing <zstd.h> from libzstd-dev, please set CXXFLAGS and LDFLAGS"
13#include "external/blake3/c/blake3.h"
18is_aiff (
const String &input)
20 return input.
substr (0, 4) ==
"FORM" && input.
substr (8, 4) ==
"AIFF";
24is_wav (
const String &input)
26 return input.
substr (0, 4) ==
"RIFF" && input.
substr (8, 4) ==
"WAVE";
30is_midi (
const String &input)
32 return input.
substr (0, 4) ==
"MThd";
36is_pdf (
const String &input)
38 return input.
substr (0, 5) ==
"%PDF-";
41static constexpr const size_t MB = 1024 * 1024;
42static struct {
size_t level,
size; }
const zstd_adaptive_level[] = {
52guess_zstd_level (
size_t input_size)
55 while (zstd_adaptive_level[zal].size > input_size)
57 return zstd_adaptive_level[zal].level;
61zstd_compress (
const String &input,
int level)
63 return zstd_compress (input.
data(), input.
size(), level);
67zstd_compress (
const void *src,
size_t src_size,
int level)
69 const size_t maxosize = ZSTD_compressBound (src_size);
71 data.resize (maxosize);
72 const size_t osize = ZSTD_compress (&data[0],
data.size(), src, src_size, level ? level : guess_zstd_level (src_size));
73 if (ZSTD_isError (osize))
75 warning (
"zstd compression failed (input=%zu): %s", src_size, ZSTD_getErrorName (osize));
84zstd_target_size (
const String &input)
86 const size_t maxosize = ZSTD_getFrameContentSize (&input[0], input.
size());
87 if (maxosize == ZSTD_CONTENTSIZE_ERROR)
89 if (maxosize == ZSTD_CONTENTSIZE_UNKNOWN)
91 if (maxosize >= 2 * 1024 * 1024 * 1024ull)
97zstd_uncompress (
const String &input,
void *dst,
size_t dst_size)
99 const ssize_t target_size = zstd_target_size (input);
101 assert_return (target_size >= 0 && target_size <= dst_size, -ENOMEM);
102 const size_t osize = ZSTD_decompress (dst, dst_size, input.
data(), input.
size());
108zstd_uncompress (
const String &input)
110 const ssize_t maxosize = zstd_target_size (input);
113 data.resize (maxosize);
114 const size_t osize = ZSTD_decompress (&data[0],
data.size(), &input[0], input.
size());
115 if (ZSTD_isError (osize))
117 warning (
"zstd decompression failed (input=%zu): %s", input.
size(), ZSTD_getErrorName (osize));
127is_zstd (
const String &input)
129 return (input.
size() >= 4 &&
130 input[0] ==
char (0x28) &&
131 input[1] ==
char (0xb5) &&
132 input[2] ==
char (0x2f) &&
133 input[3] ==
char (0xfd));
137is_lz4 (
const String &input)
139 return (input.
size() >= 4 &&
140 input[0] ==
char (0x04) &&
141 input[1] ==
char (0x22) &&
142 input[2] ==
char (0x4d) &&
143 input[3] ==
char (0x18));
147is_zip (
const String &input)
149 return (input.
size() >= 4 &&
150 input[0] ==
'P' && input[1] ==
'K' &&
151 ((input[2] == 0x03 && input[3] == 0x04) ||
152 (input[2] == 0x05 && input[3] == 0x06) ||
153 (input[2] == 0x07 && input[3] == 0x08)));
157is_arj (
const String &input)
159 return input.
size() >= 2 && input[0] ==
char (0x60) && input[1] ==
char (0xea);
163is_isz (
const String &input)
165 return input.
substr (0, 4) ==
"IsZ!";
169is_ogg (
const String &input)
171 return input.
substr (0, 4) ==
"OggS";
175is_avi (
const String &input)
177 return input.
substr (0, 4) ==
"RIFF" && input.
substr (8, 4) ==
"AVI ";
181is_gz (
const String &input)
183 return input.
size() >= 2 && input[0] ==
char (0x1f) && input[1] ==
char (0x8B);
187is_xz (
const String &input)
189 return (input.
size() >= 6 &&
190 input[0] ==
char (0xfd) &&
191 input[1] ==
char (0x37) &&
192 input[2] ==
char (0x7a) &&
193 input[3] ==
char (0x58) &&
194 input[4] ==
char (0x5a) &&
195 input[5] ==
char (0x00));
199is_png (
const String &input)
201 return (input.
size() >= 8 &&
202 input[0] ==
char (0x89) &&
203 input.
substr (1, 3) ==
"PNG" &&
204 input[4] ==
char (0x0d) &&
205 input[5] ==
char (0x0a) &&
206 input[6] ==
char (0x1a) &&
207 input[7] ==
char (0x0a));
211is_jpg (
const String &input)
213 return (input.
size() >= 4 &&
214 input[0] ==
char (0xff) &&
215 input[1] ==
char (0xd8) &&
216 input[2] ==
char (0xff) &&
217 (input[3] ==
char (0xdb) || input[3] ==
char (0xe0) ||
218 input[3] ==
char (0xee) || input[3] ==
char (0xe1)));
222is_compressed (
const String &input)
224 return (is_zstd (input) ||
239 StreamReaderP istream_;
241 ZSTD_inBuffer zinput_ = {
nullptr, 0, 0 };
242 ZSTD_DCtx *dctx_ =
nullptr;
248 name_ = istream_->name();
249 ibuffer_.resize (ZSTD_DStreamInSize());
250 dctx_ = ZSTD_createDCtx();
258 name()
const override
263 read (
void *buffer,
size_t len)
override
268 if (zinput_.pos < zinput_.size) {
269 ZSTD_outBuffer zoutput = { buffer, len, 0 };
270 ret = ZSTD_decompressStream (dctx_, &zoutput, &zinput_);
271 if (ZSTD_isError (ret))
277 while (zinput_.pos == zinput_.size && istream_) {
278 const ssize_t l = istream_->read (&ibuffer_[0], ibuffer_.size());
280 zinput_ = { &ibuffer_[0],
size_t (l), 0 };
283 ZSTD_outBuffer zoutput = { buffer, len, 0 };
284 ret = ZSTD_decompressStream (dctx_, &zoutput, &zinput_);
285 if (ZSTD_isError (ret))
291 printerr (
"%s: ZSTD_decompressStream: %s\n",
program_alias(), ZSTD_getErrorName (ret));
300 const bool closeok = istream_->close();
303 ibuffer_.reserve (0);
305 ZSTD_freeDCtx (dctx_);
312stream_reader_zstd (StreamReaderP &istream)
318static constexpr bool PRINT_ADAPTIVE =
false;
321 StreamWriterP ostream_;
324 ZSTD_CCtx *cctx_ =
nullptr;
327 bool last_block_ =
false;
333 obuffer_.reserve (0);
335 ZSTD_freeCCtx (cctx_);
340 obuffer_.resize (ZSTD_CStreamOutSize());
341 cctx_ = ZSTD_createCCtx();
344 pret = ZSTD_CCtx_setParameter (cctx_, ZSTD_c_checksumFlag, 1);
347 while (zstd_adaptive_level[zal_].size <
std::numeric_limits<
decltype (zstd_adaptive_level[0].size)>::max())
349 pret = ZSTD_CCtx_setParameter (cctx_, ZSTD_c_compressionLevel, level);
350 if (PRINT_ADAPTIVE) printerr (
"ZSTD_compressStream2: fixed level=%u: %s\n", level, ZSTD_getErrorName (pret));
352 pret = ZSTD_CCtx_setParameter (cctx_, ZSTD_c_compressionLevel, zstd_adaptive_level[zal_].level);
353 if (PRINT_ADAPTIVE) printerr (
"ZSTD_compressStream2: size=%u level=%u: %s\n", itotal_, zstd_adaptive_level[zal_].level, ZSTD_getErrorName (pret));
357 name_ = ostream_->name();
360 name()
const override
365 write (
const void *buffer,
size_t len)
override
369 const ZSTD_EndDirective mode = last_block_ ? ZSTD_e_end : ZSTD_e_continue;
370 ZSTD_inBuffer zinput = { buffer, len, 0 };
372 bool block_finished =
false;
373 while (!block_finished) {
374 ZSTD_outBuffer zoutput = { &obuffer_[0], obuffer_.size(), 0 };
376 const size_t current_total = itotal_ + zinput.pos;
377 if (!last_block_ && current_total > zstd_adaptive_level[zal_].size)
380 const size_t pret = ZSTD_CCtx_setParameter (cctx_, ZSTD_c_compressionLevel, zstd_adaptive_level[zal_].level);
382 if (PRINT_ADAPTIVE) printerr (
"ZSTD_compressStream2: size=%u level=%u: %s\n", current_total, zstd_adaptive_level[zal_].level, ZSTD_getErrorName (pret));
383 zinput.size = zinput.pos;
384 ret = ZSTD_compressStream2 (cctx_, &zoutput, &zinput, ZSTD_e_end);
388 ret = ZSTD_compressStream2 (cctx_, &zoutput, &zinput, mode);
390 if (ZSTD_isError (ret))
392 const char *p = (
const char*) &obuffer_[0], *
const e = p + zoutput.pos;
394 const ssize_t n = ostream_->write (p, e - p);
399 block_finished = last_block_ ? ret == 0 : zinput.pos == zinput.size;
401 itotal_ += zinput.pos;
404 printerr (
"%s: ZSTD_compressStream2: %s\n",
program_alias(), ZSTD_getErrorName (ret));
415 bool closedok =
true;
422 l = write (
nullptr, 0);
425 closedok &= ostream_->close();
433stream_writer_zstd (
const StreamWriterP &ostream,
int level)
440blake3_hash_string (
const String &input)
442 blake3_hasher hasher;
443 blake3_hasher_init (&hasher);
444 blake3_hasher_update (&hasher, input.
data(), input.
size());
445 uint8_t output[BLAKE3_OUT_LEN];
446 blake3_hasher_finalize (&hasher, output, BLAKE3_OUT_LEN);
447 blake3_hasher_reset (&hasher);
448 return String ((
const char*) output, BLAKE3_OUT_LEN);
452blake3_hash_file (
const String &filename)
454 StreamReaderP stream = stream_reader_from_file (filename);
456 blake3_hasher hasher;
457 blake3_hasher_init (&hasher);
459 ssize_t l = stream->read (buffer,
sizeof (buffer));
461 blake3_hasher_update (&hasher, buffer, l);
462 l = stream->read (buffer,
sizeof (buffer));
464 uint8_t output[BLAKE3_OUT_LEN];
465 blake3_hasher_finalize (&hasher, output, BLAKE3_OUT_LEN);
466 blake3_hasher_reset (&hasher);
467 return String ((
const char*) output, BLAKE3_OUT_LEN);
480 TCMP (h, ==,
"af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262");
482 TASSERT (h ==
"6201e8ededb2f1f2b6362119b46b404e822efbd58d7922202408025c5f527c56");
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
The Anklang C++ API namespace.
String string_to_hex(const String &input)
Convert bytes in string input to hexadecimal numbers.
String program_alias()
Retrieve the program name as used for logging or debug messages.
std::string String
Convenience alias for std::string.
uint32_t uint
Provide 'uint' as convenience type.
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.
#define TCMP(a, cmp, b)
Compare a and b according to operator cmp, verbose on failiure.