Compress.cpp 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998
  1. /******************************************************************************
  2. 'UIntPtr' was chosen for in-memory compression, to achieve:
  3. -full speed at 32-bit platforms (UInt)
  4. -32-bit platforms can't do 64-bit in-memory compressions
  5. -64-bit support on 64-bit platforms
  6. 'Long' was chosen for stream compression, to achieve:
  7. -64-bit support on both 32/64
  8. Stream based LZ4 uses custom block separation (not LZ4Frame API, because that one is inefficient, generates UInt chunk sizes instead of cmpUIntV)
  9. Some functions have 'NOINLINE' because they use a lot of stack memory,
  10. and some compilers (Apple) when trying to inline them, would cause a crash.
  11. Some functions use 'MemWrote' for direct decompression, and some use 'dest.skip' with 'MemFinished'.
  12. LZ4 and ZSTD require the previous content of dest buffer to be still accessible, so encryption needs to be performed at the end.
  13. /******************************************************************************/
  14. #include "stdafx.h"
  15. #define SUPPORT_LIZARD 0 // disable because LZ4 gives similar results
  16. #define SUPPORT_BROTLI 0 // disable because ZSTD is considered better
  17. #include "../../../ThirdPartyLibs/begin.h"
  18. #if SUPPORT_SNAPPY
  19. #include "../../../ThirdPartyLibs/Snappy/snappy-c.h"
  20. #endif
  21. #if SUPPORT_LZ4
  22. #if __clang__
  23. #define LZ4_DISABLE_DEPRECATE_WARNINGS // fails to compile without it
  24. #endif
  25. #include "../../../ThirdPartyLibs/LZ4/lz4.h"
  26. #include "../../../ThirdPartyLibs/LZ4/lz4hc.h"
  27. #endif
  28. #if SUPPORT_LIZARD
  29. #include "../../../ThirdPartyLibs/Lizard/lib/lizard_compress.h"
  30. #include "../../../ThirdPartyLibs/Lizard/lib/lizard_decompress.h"
  31. #endif
  32. #if SUPPORT_ZSTD
  33. #define ZSTD_STATIC_LINKING_ONLY
  34. #define ZSTD_WINDOWLOG_SAVE 0 // !! do not change in the future !!
  35. #include "../../../ThirdPartyLibs/Zstd/lib/zstd.h"
  36. #endif
  37. #if SUPPORT_BROTLI
  38. #include "../../../ThirdPartyLibs/Brotli/lib/enc/encode.h"
  39. #include "../../../ThirdPartyLibs/Brotli/lib/dec/decode.h"
  40. #endif
  41. #if SUPPORT_LZMA
  42. extern "C"
  43. {
  44. #define Bool LZMABool
  45. #define UInt32 LZMAUInt32
  46. #include "../../../ThirdPartyLibs/LZMA/lzma/C/LzmaDec.h"
  47. #include "../../../ThirdPartyLibs/LZMA/lzma/C/LzmaEnc.h"
  48. #define LZMA2 0 // no significant improvement over LZMA
  49. #if LZMA2
  50. #include "../../../ThirdPartyLibs/LZMA/lzma/C/Lzma2Dec.h"
  51. #include "../../../ThirdPartyLibs/LZMA/lzma/C/Lzma2Enc.h"
  52. #endif
  53. #undef Bool
  54. #undef UInt32
  55. }
  56. #endif
  57. #if SUPPORT_LZHAM
  58. #include "../../../ThirdPartyLibs/LZHAM/Source/lzham_comp.h"
  59. #include "../../../ThirdPartyLibs/LZHAM/Source/lzham_decomp.h"
  60. #endif
  61. #if SUPPORT_ZLIB
  62. #if WINDOWS
  63. #include "../../../ThirdPartyLibs/zlib/zlib.h" // use custom zlib library on windows
  64. #else
  65. #include <zlib.h> // use built-in zlib library on other platforms
  66. #endif
  67. #endif
  68. #include "../../../ThirdPartyLibs/end.h"
  69. #pragma warning(disable:4267) // conversion from 'size_t' to 'Int', possible loss of data
  70. #define BUF_SIZE 65536
  71. /******************************************************************************/
  72. namespace EE{
  73. /******************************************************************************/
  74. static Bool MemDecompress(File &f, Long decompressed_size)
  75. {
  76. return f._type==FILE_MEM // continuous memory
  77. && f.left()>=decompressed_size // file has enough room for decompressed data
  78. && decompressed_size>=0; // decompressed size is known
  79. }
  80. static Bool MemWrote(File &f, UIntPtr size)
  81. {
  82. if(f._cipher)f._cipher->encrypt(f.memFast(), f.memFast(), size, f.posCipher());
  83. return f.skip(size);
  84. }
  85. static void MemFinished(File &f, Ptr mem, Long wrote, Int cipher_offset)
  86. {
  87. if(f._cipher)f._cipher->encrypt(mem, mem, wrote, cipher_offset);
  88. }
  89. static Ptr CompressAlloc(Ptr p, size_t size) {return Alloc(size );}
  90. static void CompressFree (Ptr p, Ptr address) { Free (address);}
  91. /******************************************************************************/
  92. // ZLIB
  93. /******************************************************************************/
  94. #if SUPPORT_ZLIB
  95. #define ZLIB_COMPRESSBOUND(x) ((x) + ((x)>>12) + ((x)>>14) + ((x)>>25) + 13) // taken from source code
  96. static UIntPtr ZLIBSize(UIntPtr src_size) {return compressBound(src_size);} // get size needed for 'dest' buffer when compressing 'src_size' bytes
  97. static Bool ZLIBCompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size, Int compression_level) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, 'compression_level'=(0..9) (0=fastest/worst, 9=slowest/best), false on fail
  98. {
  99. uLongf size=dest_size; Bool ok=(compress2((Byte*)dest, &size, (Byte*)src, src_size, Mid(compression_level, 1, 9))==Z_OK);
  100. dest_size= size; return ok;
  101. }
  102. static Bool ZLIBDecompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  103. {
  104. uLongf size=dest_size; Bool ok=(uncompress((Byte*)dest, &size, (Byte*)src, src_size)==Z_OK);
  105. dest_size= size; return ok;
  106. }
  107. /******************************************************************************/
  108. NOINLINE static Bool ZLIBCompressMem(File &src, File &dest, Int compression_level)
  109. {
  110. Memt<Byte, BUF_SIZE > s;
  111. Memt<Byte, ZLIB_COMPRESSBOUND(BUF_SIZE)> d;
  112. s.setNum(src.left()); if(!src.getFast(s.data(), s.elms()))return false;
  113. d.setNum(ZLIBSize(s.elms())); UIntPtr dest_size=d.elms();
  114. if(ZLIBCompress(s.data(), s.elms(), d.data(), dest_size, compression_level))
  115. {
  116. s.del(); // delete 's' first to release memory
  117. return dest.put(d.data(), dest_size);
  118. }
  119. return false;
  120. }
  121. NOINLINE static Bool ZLIBDecompressMem(File &src, File &dest, Long compressed_size, Long decompressed_size) // decompress data from 'src' file into 'dest' file, 'src' should be already opened for reading, 'dest' should be already opened for writing, 'compressed_size' must be correctly specified in order to decompress the data, false on fail
  122. {
  123. Memt<Byte, ZLIB_COMPRESSBOUND(BUF_SIZE)> s; s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))return false;
  124. if(MemDecompress(dest, decompressed_size))
  125. {
  126. UIntPtr dest_size=decompressed_size;
  127. if(ZLIBDecompress(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)return MemWrote(dest, dest_size);
  128. }else
  129. {
  130. Memt<Byte, BUF_SIZE> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  131. if(ZLIBDecompress(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  132. {
  133. s.del(); // delete 's' first to release memory
  134. return dest.put(d.data(), dest_size);
  135. }
  136. }
  137. return false;
  138. }
  139. /******************************************************************************/
  140. NOINLINE static Bool ZLIBCompress(File &src, File &dest, Int compression_level, DataCallback *callback) // compress data from 'src' file into 'dest' file, 'src' should be already opened for reading, 'dest' should be already opened for writing, 'compression_level'=(0..9) (0=fastest/worst, 9=slowest/best), compression occurs from the current position of 'src' to the end of the file, false on fail
  141. {
  142. Bool ok=false;
  143. z_stream stream; Zero(stream);
  144. if(deflateInit(&stream, Mid(compression_level, 1, 9))==Z_OK) // 0=no compression
  145. {
  146. Byte in[BUF_SIZE], out[BUF_SIZE];
  147. for(;;)
  148. {
  149. Int read=src.getReturnSize(in, SIZE(in)); // always proceed even if 'read'==0 in case of empty files or if we've encountered end of file
  150. if(callback)callback->data(in, read);
  151. stream.avail_in=read;
  152. stream. next_in=in;
  153. Int status;
  154. do{
  155. stream.avail_out=SIZE(out);
  156. stream. next_out= out ;
  157. status=deflate(&stream, src.end() ? Z_FINISH : Z_NO_FLUSH);
  158. if(status==Z_STREAM_ERROR)goto error;
  159. if(!dest.put(out, SIZE(out)-stream.avail_out))goto error;
  160. }while(stream.avail_out==0);
  161. if( stream.avail_in)goto error; // if there's still some input left, then it failed
  162. if(src.end()){if(status==Z_STREAM_END)ok=true; break;} // finished
  163. if(read<=0 )goto error; // if not end of 'src' but failed to read anything, then it failed
  164. }
  165. error:
  166. deflateEnd(&stream);
  167. }
  168. return ok;
  169. }
  170. NOINLINE static Bool ZLIBDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback) // decompress data from 'src' file into 'dest' file, 'src' should be already opened for reading, 'dest' should be already opened for writing, 'compressed_size' must be correctly specified in order to decompress the data, false on fail
  171. {
  172. Bool ok=false;
  173. z_stream stream; Zero(stream);
  174. if(inflateInit(&stream)==Z_OK)
  175. {
  176. Long start=dest.pos();
  177. Byte in[BUF_SIZE], out[BUF_SIZE];
  178. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  179. Bool direct=MemDecompress(dest, decompressed_size);
  180. for(;;)
  181. {
  182. Int read=src.getReturnSize(in, SIZE(in)); if(read<=0)goto error;
  183. stream.avail_in=read;
  184. stream. next_in=in;
  185. Int status=Z_STREAM_END; // initialize to OK in case we finish early due to 'break' in 'direct' mode
  186. do{
  187. UInt avail_out; Ptr next_out;
  188. if(direct)
  189. {
  190. avail_out=dest.left (); if(!avail_out)break; // if reached end of file (this extra check is needed because the main loop continues as long as avail_out==0)
  191. next_out=dest.memFast();
  192. }else
  193. {
  194. avail_out=SIZE(out);
  195. next_out= out ;
  196. }
  197. stream.avail_out= avail_out;
  198. stream. next_out=(Byte*)next_out;
  199. status=inflate(&stream, Z_NO_FLUSH);
  200. switch(status)
  201. {
  202. case Z_NEED_DICT :
  203. case Z_STREAM_ERROR:
  204. case Z_DATA_ERROR :
  205. case Z_MEM_ERROR : goto error;
  206. }
  207. avail_out-=stream.avail_out; // now 'avail_out' = how much decoded
  208. if(callback)callback->data(next_out, avail_out);
  209. if(direct ? !MemWrote(dest, avail_out)
  210. : !dest.put(out , avail_out))goto error;
  211. }while(stream.avail_out==0);
  212. if(status==Z_STREAM_END){if(dest.pos()-start==decompressed_size)ok=true; break;}
  213. }
  214. error:
  215. src.unlimit(temp_size, temp_offset);
  216. inflateEnd(&stream);
  217. }
  218. return ok;
  219. }
  220. #endif
  221. /******************************************************************************/
  222. // LZMA
  223. /******************************************************************************/
  224. #if SUPPORT_LZMA
  225. static ISzAlloc LzmaMem={CompressAlloc, CompressFree};
  226. /******************************************************************************/
  227. struct StreamIn : ISeqInStream
  228. {
  229. File &file;
  230. DataCallback *callback;
  231. static SRes Get(Ptr stream, Ptr data, size_t *size) {return ((StreamIn*)stream)->get(data, size);}
  232. SRes get( Ptr data, size_t *size) {*size=file.getReturnSize(data, *size); if(callback)callback->data(data, *size); return SZ_OK;}
  233. StreamIn(File &file, DataCallback *callback) : file(file), callback(callback) {Read=Get;}
  234. };
  235. struct StreamOut : ISeqOutStream
  236. {
  237. File &file;
  238. static size_t Put(Ptr stream, CPtr data, size_t size) {return ((StreamOut*)stream)->put(data, size);}
  239. size_t put( CPtr data, Int size) {return file.putReturnSize(data, size);}
  240. StreamOut(File &file) : file(file) {Write=Put;}
  241. };
  242. /******************************************************************************/
  243. NOINLINE static SRes Decode(CLzmaDec *state, File &dest, File &src, Long decompressed_size, DataCallback *callback)
  244. {
  245. Bool known_size=(decompressed_size>=0), direct=MemDecompress(dest, decompressed_size);
  246. Byte inBuf[BUF_SIZE], outBuf[BUF_SIZE];
  247. size_t inPos=0, inSize=0;
  248. SRes res;
  249. LzmaDec_Init(state);
  250. for(;;)
  251. {
  252. if(inPos==inSize) // if entire 'in' buffer was processed
  253. {
  254. inPos =0;
  255. inSize=src.getReturnSize(inBuf, SIZE(inBuf));
  256. }
  257. SizeT inProcessed=inSize-inPos, outProcessed;
  258. Byte *out;
  259. if(direct)
  260. {
  261. out =(Byte*)dest.memFast();
  262. outProcessed=dest.left ();
  263. }else
  264. {
  265. out = outBuf ;
  266. outProcessed=SIZE(outBuf);
  267. }
  268. ELzmaFinishMode finishMode=LZMA_FINISH_ANY;
  269. if(known_size && outProcessed>decompressed_size)
  270. {
  271. outProcessed=decompressed_size;
  272. finishMode=LZMA_FINISH_END;
  273. }
  274. ELzmaStatus status; res=LzmaDec_DecodeToBuf(state, out, &outProcessed, inBuf+inPos, &inProcessed, finishMode, &status);
  275. if(callback)callback->data(out, outProcessed);
  276. if(direct ? !MemWrote(dest , outProcessed)
  277. : !dest.put(outBuf, outProcessed)){res=SZ_ERROR_WRITE; break;}
  278. inPos += inProcessed;
  279. decompressed_size-=outProcessed;
  280. if(res!=SZ_OK || known_size && decompressed_size==0)break;
  281. if(inProcessed==0 && outProcessed==0)
  282. {
  283. if(known_size || status!=LZMA_STATUS_FINISHED_WITH_MARK)res=SZ_ERROR_DATA;
  284. break;
  285. }
  286. }
  287. return res;
  288. }
  289. #if LZMA2
  290. NOINLINE static SRes Decode2(CLzma2Dec *state, File &dest, File &src, Long decompressed_size, DataCallback *callback)
  291. {
  292. Bool known_size=(decompressed_size>=0), direct=MemDecompress(dest, decompressed_size);
  293. Byte inBuf[BUF_SIZE], outBuf[BUF_SIZE];
  294. size_t inPos=0, inSize=0;
  295. Lzma2Dec_Init(state);
  296. for(;;)
  297. {
  298. if(inPos==inSize) // if entire 'in' buffer was processed
  299. {
  300. inPos =0;
  301. inSize=src.getReturnSize(inBuf, SIZE(inBuf));
  302. }
  303. SizeT inProcessed=inSize-inPos, outProcessed;
  304. Byte *out;
  305. if(direct)
  306. {
  307. out =(Byte*)dest.memFast();
  308. outProcessed=dest.left ();
  309. }else
  310. {
  311. out = outBuf ;
  312. outProcessed=SIZE(outBuf);
  313. }
  314. ELzmaFinishMode finishMode=LZMA_FINISH_ANY;
  315. if(known_size && outProcessed>decompressed_size)
  316. {
  317. outProcessed=decompressed_size;
  318. finishMode=LZMA_FINISH_END;
  319. }
  320. ELzmaStatus status; SRes res=Lzma2Dec_DecodeToBuf(state, out, &outProcessed, inBuf+inPos, &inProcessed, finishMode, &status);
  321. if(callback)callback->data(out, outProcessed);
  322. if(direct ? !MemWrote(dest , outProcessed)
  323. : !dest.put(outBuf, outProcessed))return SZ_ERROR_WRITE;
  324. inPos += inProcessed;
  325. decompressed_size-=outProcessed;
  326. if(res!=SZ_OK || known_size && decompressed_size==0)return res;
  327. if(inProcessed==0 && outProcessed==0)
  328. {
  329. if(known_size || status!=LZMA_STATUS_FINISHED_WITH_MARK)return SZ_ERROR_DATA;
  330. return res;
  331. }
  332. }
  333. }
  334. #endif
  335. static SRes Decode(File &dest, File &src, Long decompressed_size, DataCallback *callback)
  336. {
  337. Byte header[LZMA_PROPS_SIZE]; if(!src.getFast(header))return SZ_ERROR_READ;
  338. CLzmaDec state; LzmaDec_Construct(&state);
  339. RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &LzmaMem));
  340. SRes res=Decode(&state, dest, src, decompressed_size, callback);
  341. LzmaDec_Free(&state, &LzmaMem);
  342. return res;
  343. }
  344. #if LZMA2
  345. static SRes Decode2(File &dest, File &src, Long decompressed_size, DataCallback *callback)
  346. {
  347. Byte prop; if(!src.getFast(prop))return SZ_ERROR_READ;
  348. CLzma2Dec state; Lzma2Dec_Construct(&state);
  349. RINOK(Lzma2Dec_Allocate(&state, prop, &LzmaMem));
  350. SRes res=Decode2(&state, dest, src, decompressed_size, callback);
  351. Lzma2Dec_Free(&state, &LzmaMem);
  352. return res;
  353. }
  354. #endif
  355. /******************************************************************************/
  356. static void SetProps(CLzmaEncProps &props, Int compression_level, Bool multi_threaded, Long uncompressed_size)
  357. {
  358. LzmaEncProps_Init(&props);
  359. props.writeEndMark=false; // we don't need to write the marker, because we set both 'File.limit' and 'decompressed_size', disabling this results in 5 less bytes
  360. props.level=Mid(compression_level, 0, 9);
  361. if(uncompressed_size>=0)props.reduceSize=uncompressed_size; // if size is known, this will greatly reduce memory usage for small files but with high 'compression_level'
  362. props.numThreads=((multi_threaded && Cpu.threads()>1) ? 2 : 1);
  363. }
  364. #if LZMA2
  365. static void SetProps(CLzma2EncProps &props, Int compression_level, Bool multi_threaded, Long uncompressed_size)
  366. {
  367. Lzma2EncProps_Init(&props);
  368. props.lzmaProps.writeEndMark=false; // we don't need to write the marker, because we set both 'File.limit' and 'decompressed_size', disabling this results in 5 less bytes
  369. props.lzmaProps.level=Mid(compression_level, 0, 9);
  370. if(uncompressed_size>=0)props.lzmaProps.reduceSize=uncompressed_size; // if size is known, this will greatly reduce memory usage for small files but with high 'compression_level'
  371. // TODO: test these settings
  372. props.lzmaProps.numThreads=((multi_threaded && Cpu.threads()>1) ? 2 : 1);
  373. //props.numBlockThreads=-1;
  374. //props.numTotalThreads=-1;
  375. }
  376. #endif
  377. /******************************************************************************/
  378. static UInt LZMADictSize(Int compression_level, Long size)
  379. {
  380. CLzmaEncProps props; SetProps(props, compression_level, false, size);
  381. return LzmaEncProps_GetDictSize(&props);
  382. }
  383. static Bool LZMACompress(File &src, File &dest, Int compression_level, Bool multi_threaded, DataCallback *callback)
  384. {
  385. Bool ok=false;
  386. if(CLzmaEncHandle enc=LzmaEnc_Create(&LzmaMem))
  387. {
  388. CLzmaEncProps props; SetProps(props, compression_level, multi_threaded, src.left());
  389. if(LzmaEnc_SetProps(enc, &props)==SZ_OK)
  390. {
  391. Byte header[LZMA_PROPS_SIZE];
  392. size_t headerSize=LZMA_PROPS_SIZE;
  393. if(LzmaEnc_WriteProperties(enc, header, &headerSize)==SZ_OK)
  394. if(dest.put(header, headerSize))
  395. {
  396. StreamIn stream_in (src, callback);
  397. StreamOut stream_out(dest);
  398. ok=(LzmaEnc_Encode(enc, &stream_out, &stream_in, null, &LzmaMem, &LzmaMem)==SZ_OK);
  399. }
  400. }
  401. LzmaEnc_Destroy(enc, &LzmaMem, &LzmaMem);
  402. }
  403. return ok;
  404. }
  405. static Bool LZMADecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  406. {
  407. ULong size, offset; src.limit(size, offset, compressed_size);
  408. Bool ok=(Decode(dest, src, decompressed_size, callback)==SZ_OK);
  409. src.unlimit(size, offset);
  410. return ok;
  411. }
  412. #if LZMA2
  413. static Bool LZMA2Compress(File &src, File &dest, Int compression_level, Bool multi_threaded, DataCallback *callback)
  414. {
  415. Bool ok=false;
  416. if(CLzma2EncHandle enc=Lzma2Enc_Create(&LzmaMem, &LzmaMem))
  417. {
  418. CLzma2EncProps props; SetProps(props, compression_level, multi_threaded, src.left());
  419. if(Lzma2Enc_SetProps(enc, &props)==SZ_OK)
  420. {
  421. Byte prop=Lzma2Enc_WriteProperties(enc);
  422. if(dest.put(prop))
  423. {
  424. StreamIn stream_in (src, callback);
  425. StreamOut stream_out(dest);
  426. ok=(Lzma2Enc_Encode(enc, &stream_out, &stream_in, null)==SZ_OK);
  427. }
  428. }
  429. Lzma2Enc_Destroy(enc);
  430. }
  431. return ok;
  432. }
  433. static Bool LZMA2Decompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  434. {
  435. ULong size, offset; src.limit(size, offset, compressed_size);
  436. Bool ok=(Decode2(dest, src, decompressed_size, callback)==SZ_OK);
  437. src.unlimit(size, offset);
  438. return ok;
  439. }
  440. #endif
  441. #endif
  442. /******************************************************************************/
  443. // SNAPPY
  444. /******************************************************************************/
  445. #if SUPPORT_SNAPPY
  446. #define SNAPPY_BUF_SIZE 65536 // chunks to support streaming !! don't change in the future !!
  447. #define SNAPPY_COMPRESSBOUND(x) (32 + (x) + (x)/6) // taken from Snappy source code
  448. static UIntPtr SNAPPYSize(UIntPtr src_size) {return snappy_max_compressed_length(src_size);}
  449. static Bool SNAPPYCompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, false on fail
  450. {
  451. size_t size=dest_size; Bool ok=(snappy_compress((char*)src, src_size, (char*)dest, &size)==SNAPPY_OK);
  452. dest_size= size; return ok;
  453. }
  454. static Bool SNAPPYDecompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  455. {
  456. size_t size=dest_size; Bool ok=(snappy_uncompress((char*)src, src_size, (char*)dest, &size)==SNAPPY_OK);
  457. dest_size= size; return ok;
  458. }
  459. /******************************************************************************/
  460. NOINLINE static Bool SNAPPYCompressMem(File &src, File &dest, DataCallback *callback)
  461. {
  462. Memt<Byte, SNAPPY_BUF_SIZE > s; s.setNum(src.left()); if(src.getFast(s.data(), s.elms()))
  463. {
  464. Memt<Byte, SNAPPY_COMPRESSBOUND(SNAPPY_BUF_SIZE)> d; d.setNum(SNAPPYSize(s.elms())); UIntPtr dest_size=d.elms();
  465. if(SNAPPYCompress(s.data(), s.elms(), d.data(), dest_size))
  466. {
  467. if(callback)callback->data(s.data(), s.elms());
  468. s.del(); // delete 's' first to release memory
  469. return dest.put(d.data(), dest_size);
  470. }
  471. }
  472. return false;
  473. }
  474. NOINLINE static Bool SNAPPYDecompressMem(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  475. {
  476. Memt<Byte, SNAPPY_COMPRESSBOUND(SNAPPY_BUF_SIZE)> s;
  477. s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))goto error;
  478. if(MemDecompress(dest, decompressed_size))
  479. {
  480. UIntPtr dest_size=decompressed_size;
  481. if(SNAPPYDecompress(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)
  482. {
  483. if(callback)callback->data(dest.memFast(), dest_size);
  484. return MemWrote(dest, dest_size);
  485. }
  486. }else
  487. {
  488. Memt<Byte, SNAPPY_BUF_SIZE> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  489. if(SNAPPYDecompress(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  490. {
  491. s.del(); // delete 's' first to release memory
  492. if(callback)callback->data(d.data(), dest_size);
  493. return dest.put(d.data(), dest_size);
  494. }
  495. }
  496. error:
  497. return false;
  498. }
  499. /******************************************************************************/
  500. NOINLINE static Bool SNAPPYCompressStream(File &src, File &dest, DataCallback *callback)
  501. {
  502. Byte s[SNAPPY_BUF_SIZE], d[SNAPPY_COMPRESSBOUND(SNAPPY_BUF_SIZE)];
  503. for(; !src.end(); )
  504. {
  505. Int read=src.getReturnSize(s, SIZE(s)); if(read<=0)goto error;
  506. UIntPtr dest_size=SIZE(d); if(!SNAPPYCompress(s, read, d, dest_size))goto error;
  507. if(callback)callback->data(s, read);
  508. dest.cmpUIntV(dest_size-1); if(!dest.put(d, dest_size))goto error;
  509. }
  510. return dest.ok();
  511. error:
  512. return false;
  513. }
  514. NOINLINE static Bool SNAPPYDecompressStream(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  515. {
  516. Byte s[SNAPPY_COMPRESSBOUND(SNAPPY_BUF_SIZE)], d[SNAPPY_BUF_SIZE];
  517. Bool ok=false, direct=MemDecompress(dest, decompressed_size);
  518. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  519. Long start=dest.pos();
  520. for(; !src.end(); )
  521. {
  522. UInt chunk=src.decUIntV()+1; if(chunk>SIZE(s))goto error;
  523. if(!src.getFast(s, chunk))goto error; // needs exactly 'chunk' amount
  524. if(direct)
  525. {
  526. UIntPtr dest_size=dest.left(); if(!SNAPPYDecompress(s, chunk, dest.memFast(), dest_size))goto error;
  527. if(callback)callback->data(dest.memFast(), dest_size);
  528. if(!MemWrote(dest, dest_size))goto error;
  529. }else
  530. {
  531. UIntPtr dest_size=SIZE(d); if(!SNAPPYDecompress(s, chunk, d, dest_size))goto error;
  532. if(callback)callback->data(d, dest_size);
  533. if(!dest.put(d, dest_size))goto error;
  534. }
  535. }
  536. if(src.ok() && dest.pos()-start==decompressed_size)ok=true;
  537. error:
  538. src.unlimit(temp_size, temp_offset);
  539. return ok;
  540. }
  541. /******************************************************************************/
  542. // keep as separate functions, because both of them use a lot of stack memory which could crash if combined together
  543. static Bool SNAPPYCompress(File &src, File &dest, DataCallback *callback)
  544. {
  545. return (src.left()<=SNAPPY_BUF_SIZE) ? SNAPPYCompressMem (src, dest, callback) // for small files prefer in-memory compression to avoid writing chunk sizes !! once placed here, don't change in the future because will break compatibility !!
  546. : SNAPPYCompressStream(src, dest, callback);
  547. }
  548. static Bool SNAPPYDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  549. {
  550. return (decompressed_size<=SNAPPY_BUF_SIZE) ? SNAPPYDecompressMem (src, dest, compressed_size, decompressed_size, callback) // for small files prefer in-memory compression to avoid writing chunk sizes !! once placed here, don't change in the future because will break compatibility !!
  551. : SNAPPYDecompressStream(src, dest, compressed_size, decompressed_size, callback);
  552. }
  553. #endif
  554. /******************************************************************************/
  555. // RLE
  556. /******************************************************************************/
  557. #if SUPPORT_RLE
  558. Int CmpUIntVSize(UInt u)
  559. {
  560. Int i=1; for(; u>=128; u>>=7)i++;
  561. return i;
  562. }
  563. static void CmpUIntV(UInt u, Byte *data, UIntPtr &pos)
  564. {
  565. data[pos++]=(u&127)|((u>=128)<<7);
  566. if(u>=128)
  567. {
  568. u>>=7; data[pos++]=((u&127)|((u>=128)<<7));
  569. if(u>=128)
  570. {
  571. u>>=7; data[pos++]=((u&127)|((u>=128)<<7));
  572. if(u>=128)
  573. {
  574. u>>=7; data[pos++]=((u&127)|((u>=128)<<7));
  575. if(u>=128)
  576. {
  577. u>>=7; data[pos++]=u;
  578. }
  579. }
  580. }
  581. }
  582. }
  583. static Bool DecUIntV(UInt &u, C Byte *data, UIntPtr size, UIntPtr &pos)
  584. {
  585. if(!InRange(pos, size))return false; Byte v=data[pos++]; u=(v&127);
  586. if(v&128)
  587. {
  588. if(!InRange(pos, size))return false; v=data[pos++]; u|=((v&127)<<7);
  589. if(v&128)
  590. {
  591. if(!InRange(pos, size))return false; v=data[pos++]; u|=((v&127)<<(7+7));
  592. if(v&128)
  593. {
  594. if(!InRange(pos, size))return false; v=data[pos++]; u|=((v&127)<<(7+7+7));
  595. if(v&128)
  596. {
  597. if(!InRange(pos, size))return false; v=data[pos++]; u|=((v&127)<<(7+7+7+7));
  598. }
  599. }
  600. }
  601. }
  602. return true;
  603. }
  604. /******************************************************************************/
  605. static UIntPtr RLESize(UIntPtr src_size) {return src_size+MaxCmpUIntVSize;}
  606. static Bool RLECompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, false on fail
  607. {
  608. if(dest_size<RLESize(src_size))return false;
  609. IntPtr to=src_size-2;
  610. UIntPtr src_pos=0, dest_pos=0;
  611. C Byte *s=(Byte*)src;
  612. Byte *d=(Byte*)dest;
  613. for(; src_pos<src_size; )
  614. {
  615. find_equal:
  616. for(IntPtr i=src_pos; i<to; i++)
  617. {
  618. Byte b=s[i]; if(b==s[i+1] && b==s[i+2]) // find 3 equal
  619. {
  620. IntPtr different=i-src_pos; if(different>0)
  621. {
  622. UIntPtr count=(different-1)<<1, count_size=CmpUIntVSize(count);
  623. if(count_size>1) // if the counter size is big, then we need to check some extra data for matching 'b' byte, otherwise we might be losing space instead of gaining, when compressing the 3 bytes, because the 'count' marker uses size too (for example 65 different bytes, 3 same bytes, 65 different bytes, would generate 2-byte size + 65-byte data + 1-byte size + 1-byte data + 2-byte size + 65-byte data, losing 2-bytes on different data but gaining only 1-byte on same data)
  624. {
  625. IntPtr j=i+3, k=j+count_size-2+1; // j=first index to check, k=last index to check (inclusive, add extra 1 to avoid switching between same/different chunks, to avoid CPU overhead, when the size would be the same in both cases 1)one big different 2)different+same)
  626. if(!InRange(k, src_size))goto too_short_range; // out of range (skip entirely because there's no data after these 3 for a possible larger match)
  627. for(; j<=k; j++)if(s[j]!=b){i=j-1; goto too_short_data;} // different byte (continue looking for another match, set "i=j-1" because 'i' will get ++ in the 'for' loop)
  628. }
  629. // write any different before those 3
  630. CmpUIntV(count, d, dest_pos);
  631. CopyFast(d+dest_pos, s+src_pos, different); dest_pos+=different; src_pos+=different;
  632. }
  633. // calculate how many same bytes are there, now "i==src_pos"
  634. for(IntPtr j=i+3; ; j++)if(!InRange(j, src_size) || s[j]!=b) // out of range or different byte
  635. {
  636. // write i .. j-1
  637. IntPtr equal=j-i;
  638. CmpUIntV(((equal-3)<<1)|1, d, dest_pos);
  639. d[dest_pos++]=b;
  640. src_pos=j;
  641. break;
  642. }
  643. goto find_equal; // after finding equal we can proceed again with the search
  644. }
  645. too_short_data:;
  646. }
  647. too_short_range:
  648. // didn't found any 3 equal, write remaining different
  649. IntPtr different=src_size-src_pos; if(different>0)
  650. {
  651. CmpUIntV((different-1)<<1, d, dest_pos);
  652. CopyFast(d+dest_pos, s+src_pos, different); dest_pos+=different; //src_pos+=different; not needed since we're finishing
  653. }
  654. break; // finish
  655. }
  656. dest_size=dest_pos; return true;
  657. }
  658. static Bool RLEDecompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  659. {
  660. C Byte *s=(Byte*)src;
  661. Byte *d=(Byte*)dest;
  662. UIntPtr src_pos=0, dest_pos=0;
  663. for(; src_pos<src_size; )
  664. {
  665. UInt length; if(!DecUIntV(length, s, src_size, src_pos))return false;
  666. if( length&0x1)
  667. {
  668. length=(length>>1)+3;
  669. if(dest_pos+length > dest_size
  670. || src_pos >= src_size)return false;
  671. SetMem(d+dest_pos, s[src_pos++], length); dest_pos+=length;
  672. }else
  673. {
  674. length=(length>>1)+1;
  675. if(dest_pos+length > dest_size
  676. || src_pos+length > src_size)return false;
  677. CopyFast(d+dest_pos, s+src_pos, length); dest_pos+=length; src_pos+=length;
  678. }
  679. }
  680. dest_size=dest_pos; return true;
  681. }
  682. /******************************************************************************/
  683. NOINLINE static Bool RLECompressMem(File &src, File &dest)
  684. {
  685. Memt<Byte> s, d;
  686. s.setNum(src.left()); if(!src.getFast(s.data(), s.elms()))return false;
  687. d.setNum(RLESize(s.elms())); UIntPtr dest_size=d.elms();
  688. if(RLECompress(s.data(), s.elms(), d.data(), dest_size))
  689. {
  690. s.del(); // delete 's' first to release memory
  691. return dest.put(d.data(), dest_size);
  692. }
  693. return false;
  694. }
  695. NOINLINE static Bool RLEDecompressMem(File &src, File &dest, Long compressed_size, Long decompressed_size)
  696. {
  697. Memt<Byte> s;
  698. s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))return false;
  699. if(MemDecompress(dest, decompressed_size))
  700. {
  701. UIntPtr dest_size=decompressed_size;
  702. if(RLEDecompress(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)return MemWrote(dest, dest_size);
  703. }else
  704. {
  705. Memt<Byte> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  706. if(RLEDecompress(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  707. {
  708. s.del(); // delete 's' first to release memory
  709. return dest.put(d.data(), dest_size);
  710. }
  711. }
  712. return false;
  713. }
  714. /******************************************************************************/
  715. NOINLINE static Bool RLECompress(File &src, File &dest, DataCallback *callback)
  716. {
  717. Memt<Byte> sm; sm.setNum(src.left()); Byte *s=sm.data(); if(src.getFast(s, sm.elms()))
  718. {
  719. if(callback)callback->data(s, sm.elms());
  720. Int src_pos=0, to=sm.elms()-2;
  721. for(; src_pos<sm.elms(); )
  722. {
  723. find_equal:
  724. for(Int i=src_pos; i<to; i++)
  725. {
  726. Byte b=s[i]; if(b==s[i+1] && b==s[i+2]) // find 3 equal
  727. {
  728. Int different=i-src_pos; if(different>0)
  729. {
  730. UInt count=(different-1)<<1, count_size=CmpUIntVSize(count);
  731. if(count_size>1) // if the counter size is big, then we need to check some extra data for matching 'b' byte, otherwise we might be losing space instead of gaining, when compressing the 3 bytes, because the 'count' marker uses size too (for example 65 different bytes, 3 same bytes, 65 different bytes, would generate 2-byte size + 65-byte data + 1-byte size + 1-byte data + 2-byte size + 65-byte data, losing 2-bytes on different data but gaining only 1-byte on same data)
  732. {
  733. Int j=i+3, k=j+count_size-2+1; // j=first index to check, k=last index to check (inclusive, add extra 1 to avoid switching between same/different chunks, to avoid CPU overhead, when the size would be the same in both cases 1)one big different 2)different+same)
  734. if(!InRange(k, sm))goto too_short_range; // out of range (skip entirely because there's no data after these 3 for a possible larger match)
  735. for(; j<=k; j++)if(s[j]!=b){i=j-1; goto too_short_data;} // different byte (continue looking for another match, set "i=j-1" because 'i' will get ++ in the 'for' loop)
  736. }
  737. // write any different before those 3
  738. dest.cmpUIntV(count);
  739. if(!dest.put(s+src_pos, different))return false; src_pos+=different;
  740. }
  741. // calculate how many same bytes are there, now "i==src_pos"
  742. for(Int j=i+3; ; j++)if(!InRange(j, sm) || s[j]!=b) // out of range or different byte
  743. {
  744. // write i .. j-1
  745. Int equal=j-i;
  746. dest.cmpUIntV(((equal-3)<<1)|1).putByte(b);
  747. src_pos=j;
  748. break;
  749. }
  750. goto find_equal; // after finding equal we can proceed again with the search
  751. }
  752. too_short_data:;
  753. }
  754. too_short_range:
  755. // didn't found any 3 equal, write remaining different
  756. Int different=sm.elms()-src_pos; if(different>0)
  757. {
  758. dest.cmpUIntV((different-1)<<1);
  759. if(!dest.put(s+src_pos, different))return false; //src_pos+=different; not needed since we're finishing
  760. }
  761. break; // finish
  762. }
  763. return dest.ok();
  764. }
  765. return false;
  766. }
  767. static Bool RLEDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  768. {
  769. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  770. Long start=dest.pos();
  771. Bool direct=MemDecompress(dest, decompressed_size), ok=false;
  772. for(; !src.end(); )
  773. {
  774. UInt length; src.decUIntV(length);
  775. if( length&0x1)
  776. {
  777. length=(length>>1)+3;
  778. Byte b=src.getByte();
  779. if(direct)
  780. {
  781. if(dest.left()<length)goto error;
  782. SetMem(dest.memFast(), b, length);
  783. if(callback)callback->data(dest.memFast(), length);
  784. if(!MemWrote(dest, length))goto error;
  785. }else
  786. {
  787. if(callback)REP(length)callback->data(&b, SIZE(b));
  788. REP(length)dest.putByte(b);
  789. }
  790. }else
  791. {
  792. length=(length>>1)+1;
  793. if(callback ? !src.copy(dest, *callback, length)
  794. : !src.copy(dest, length))goto error;
  795. }
  796. }
  797. ok=(src.ok() && dest.ok() && dest.pos()-start==decompressed_size);
  798. error:
  799. src.unlimit(temp_size, temp_offset);
  800. return ok;
  801. }
  802. #endif
  803. /******************************************************************************/
  804. // LZ4
  805. /******************************************************************************/
  806. #if SUPPORT_LZ4
  807. #define LZ4_BUF_SIZE 65536 // headers say that up to 64Kb need to be kept in memory !! don't change in the future !!
  808. static UIntPtr LZ4Size(UIntPtr size) {return LZ4_compressBound(size);}
  809. static Bool LZ4Compress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size, Int compression_level) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, false on fail
  810. {
  811. auto size=((compression_level>0) ? LZ4_compress_HC((char*)src, (char*)dest, src_size, dest_size, compression_level) : LZ4_compress_default((char*)src, (char*)dest, src_size, dest_size));
  812. if( size>0){dest_size=size; return true;}
  813. return false;
  814. }
  815. static Bool LZ4Decompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  816. {
  817. auto size =LZ4_decompress_safe((char*)src, (char*)dest, src_size, dest_size);
  818. if( size>=0){dest_size=size; return true;} // must check for >=0 because <0 are errors
  819. return false;
  820. }
  821. /******************************************************************************/
  822. NOINLINE static Bool LZ4CompressMem(File &src, File &dest, Int compression_level, DataCallback *callback)
  823. {
  824. Memt<Byte, LZ4_BUF_SIZE> s; s.setNum(src.left()); if(src.getFast(s.data(), s.elms()))
  825. {
  826. Memt<Byte, LZ4_COMPRESSBOUND(LZ4_BUF_SIZE)> d; d.setNum(LZ4Size(s.elms())); UIntPtr dest_size=d.elms();
  827. if(LZ4Compress(s.data(), s.elms(), d.data(), dest_size, compression_level))
  828. {
  829. s.del(); // delete 's' first to release memory
  830. if(callback)callback->data(s.data(), s.elms());
  831. return dest.put(d.data(), dest_size);
  832. }
  833. }
  834. return false;
  835. }
  836. NOINLINE static Bool LZ4DecompressMem(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  837. {
  838. Memt<Byte, LZ4_COMPRESSBOUND(LZ4_BUF_SIZE)> s;
  839. s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))goto error;
  840. if(MemDecompress(dest, decompressed_size))
  841. {
  842. UIntPtr dest_size=decompressed_size;
  843. if(LZ4Decompress(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)
  844. {
  845. if(callback)callback->data(dest.memFast(), dest_size);
  846. return MemWrote(dest, dest_size);
  847. }
  848. }else
  849. {
  850. Memt<Byte, LZ4_BUF_SIZE> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  851. if(LZ4Decompress(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  852. {
  853. s.del(); // delete 's' first to release memory
  854. if(callback)callback->data(d.data(), dest_size);
  855. return dest.put(d.data(), dest_size);
  856. }
  857. }
  858. error:
  859. return false;
  860. }
  861. /******************************************************************************/
  862. NOINLINE static Bool LZ4CompressStream(File &src, File &dest, Int compression_level, DataCallback *callback)
  863. {
  864. Byte s[LZ4_BUF_SIZE*2], d[LZ4_COMPRESSBOUND(LZ4_BUF_SIZE)]; Int s_pos=0;
  865. union
  866. {
  867. LZ4_streamHC_t lz4_hc;
  868. LZ4_stream_t lz4;
  869. };
  870. if(compression_level>0)LZ4_resetStreamHC(&lz4_hc, compression_level);
  871. else LZ4_resetStream (&lz4);
  872. for(; !src.end(); )
  873. {
  874. Int read=Min(Min(LZ4_BUF_SIZE, SIZEI(s)), src.left());
  875. if(s_pos>SIZE(s)-read)s_pos=0; // if reading will exceed buffer size
  876. read=src.getReturnSize(&s[s_pos], read); if(read<=0)goto error;
  877. if(callback)callback->data(&s[s_pos], read);
  878. auto size=((compression_level>0) ? LZ4_compress_HC_continue (&lz4_hc, (char*)&s[s_pos], (char*)d, read, SIZE(d) )
  879. : LZ4_compress_fast_continue(&lz4 , (char*)&s[s_pos], (char*)d, read, SIZE(d), 1));
  880. if(size>0)
  881. {
  882. dest.cmpUIntV(size-1);
  883. if(!dest.put(d, size))goto error;
  884. }else goto error;
  885. s_pos+=read;
  886. }
  887. return dest.ok();
  888. error:
  889. return false;
  890. }
  891. NOINLINE static Bool LZ4DecompressStream(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  892. {
  893. Bool ok=false;
  894. LZ4_streamDecode_t lz4; if(LZ4_setStreamDecode(&lz4, null, 0))
  895. {
  896. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  897. Int cipher_offset=dest.posCipher();
  898. Long start=dest.pos();
  899. Ptr mem=dest.memFast();
  900. Byte s[LZ4_COMPRESSBOUND(LZ4_BUF_SIZE)], d[LZ4_BUF_SIZE*2]; Int d_pos=0;
  901. Bool direct=MemDecompress(dest, decompressed_size);
  902. for(; !src.end(); )
  903. {
  904. UInt chunk=src.decUIntV()+1; if(chunk>SIZE(s))goto error;
  905. if(!src.getFast(s, chunk))goto error; // need exactly 'chunk' amount
  906. if(direct)
  907. {
  908. auto size=LZ4_decompress_safe_continue(&lz4, (char*)s, (char*)dest.memFast(), chunk, dest.left()); if(size<0)goto error;
  909. if(callback)callback->data(dest.memFast(), size);
  910. if(!dest.skip(size))goto error; // can't use 'MemWrote' and 'Cipher' here because LZ4 may still access previously decompressed data
  911. }else
  912. {
  913. if(d_pos>SIZE(d)-LZ4_BUF_SIZE)d_pos=0; // if writing will exceed buffer size (this assumes that up to LZ4_BUF_SIZE can be written at one time)
  914. auto size=LZ4_decompress_safe_continue(&lz4, (char*)s, (char*)d+d_pos, chunk, SIZE(d)-d_pos); if(size<0)goto error;
  915. if(callback)callback->data(d+d_pos, size);
  916. if(!dest.put(d+d_pos, size))goto error; d_pos+=size;
  917. }
  918. }
  919. {
  920. Long wrote=dest.pos()-start;
  921. if(direct)MemFinished(dest, mem, wrote, cipher_offset);
  922. if(src.ok() && wrote==decompressed_size)ok=true;
  923. }
  924. error:
  925. src.unlimit(temp_size, temp_offset);
  926. }
  927. return ok;
  928. }
  929. /******************************************************************************/
  930. // keep as separate functions, because both of them use a lot of stack memory which could crash if combined together
  931. static Bool LZ4Compress(File &src, File &dest, Int compression_level, DataCallback *callback)
  932. {
  933. return (src.left()<=LZ4_BUF_SIZE) ? LZ4CompressMem (src, dest, compression_level, callback) // for small files prefer in-memory compression to avoid writing chunk sizes !! once placed here, don't change in the future because will break compatibility !!
  934. : LZ4CompressStream(src, dest, compression_level, callback);
  935. }
  936. static Bool LZ4Decompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  937. {
  938. return (decompressed_size<=LZ4_BUF_SIZE) ? LZ4DecompressMem (src, dest, compressed_size, decompressed_size, callback) // for small files prefer in-memory compression to avoid writing chunk sizes !! once placed here, don't change in the future because will break compatibility !!
  939. : LZ4DecompressStream(src, dest, compressed_size, decompressed_size, callback);
  940. }
  941. #endif
  942. /******************************************************************************/
  943. // LIZARD
  944. /******************************************************************************/
  945. #if SUPPORT_LIZARD
  946. #define LIZARD_BUF_SIZE 65536 // headers say that up to 64Kb need to be kept in memory !! don't change in the future !!
  947. static UIntPtr LIZARDSize(UIntPtr size) {return Lizard_compressBound(size);}
  948. static Bool LIZARDCompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size, Int compression_level) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, false on fail
  949. {
  950. auto size=Lizard_compress((char*)src, (char*)dest, src_size, dest_size, Mid(compression_level, LIZARD_MIN_CLEVEL, LIZARD_MAX_CLEVEL));
  951. if( size>0){dest_size=size; return true;}
  952. return false;
  953. }
  954. static Bool LIZARDDecompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  955. {
  956. auto size =Lizard_decompress_safe((char*)src, (char*)dest, src_size, dest_size);
  957. if( size>=0){dest_size=size; return true;} // must check for >=0 because <0 are errors
  958. return false;
  959. }
  960. /******************************************************************************/
  961. NOINLINE static Bool LIZARDCompressMem(File &src, File &dest, Int compression_level, DataCallback *callback)
  962. {
  963. Memt<Byte, LIZARD_BUF_SIZE> s; s.setNum(src.left()); if(src.getFast(s.data(), s.elms()))
  964. {
  965. Memt<Byte, LIZARD_COMPRESSBOUND(LIZARD_BUF_SIZE)> d; d.setNum(LIZARDSize(s.elms())); UIntPtr dest_size=d.elms();
  966. if(LIZARDCompress(s.data(), s.elms(), d.data(), dest_size, Mid(compression_level, LIZARD_MIN_CLEVEL, LIZARD_MAX_CLEVEL)))
  967. {
  968. s.del(); // delete 's' first to release memory
  969. if(callback)callback->data(s.data(), s.elms());
  970. return dest.put(d.data(), dest_size);
  971. }
  972. }
  973. return false;
  974. }
  975. NOINLINE static Bool LIZARDDecompressMem(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  976. {
  977. Memt<Byte, LIZARD_COMPRESSBOUND(LIZARD_BUF_SIZE)> s;
  978. s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))goto error;
  979. if(MemDecompress(dest, decompressed_size))
  980. {
  981. UIntPtr dest_size=decompressed_size;
  982. if(LIZARDDecompress(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)
  983. {
  984. if(callback)callback->data(dest.memFast(), dest_size);
  985. return MemWrote(dest, dest_size);
  986. }
  987. }else
  988. {
  989. Memt<Byte, LIZARD_BUF_SIZE> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  990. if(LIZARDDecompress(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  991. {
  992. s.del(); // delete 's' first to release memory
  993. if(callback)callback->data(d.data(), dest_size);
  994. return dest.put(d.data(), dest_size);
  995. }
  996. }
  997. error:
  998. return false;
  999. }
  1000. /******************************************************************************/
  1001. NOINLINE static Bool LIZARDCompressStream(File &src, File &dest, Int compression_level, DataCallback *callback)
  1002. {
  1003. Bool ok=false;
  1004. if(Lizard_stream_t *lizard=Lizard_createStream(Mid(compression_level, LIZARD_MIN_CLEVEL, LIZARD_MAX_CLEVEL)))
  1005. {
  1006. Byte s[LIZARD_BUF_SIZE*2], d[LIZARD_COMPRESSBOUND(LIZARD_BUF_SIZE)]; Int s_pos=0;
  1007. for(; !src.end(); )
  1008. {
  1009. Int read=Min(Min(LIZARD_BUF_SIZE, SIZEI(s)), src.left());
  1010. if(s_pos>SIZE(s)-read)s_pos=0; // if reading will exceed buffer size
  1011. read=src.getReturnSize(&s[s_pos], read); if(read<=0)goto error;
  1012. if(callback)callback->data(&s[s_pos], read);
  1013. auto size=Lizard_compress_continue(lizard, (char*)&s[s_pos], (char*)d, read, SIZE(d));
  1014. if(size>0)
  1015. {
  1016. dest.cmpUIntV(size-1);
  1017. if(!dest.put(d, size))goto error;
  1018. }else goto error;
  1019. s_pos+=read;
  1020. }
  1021. ok=dest.ok();
  1022. error:
  1023. Lizard_freeStream(lizard);
  1024. }
  1025. return ok;
  1026. }
  1027. NOINLINE static Bool LIZARDDecompressStream(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  1028. {
  1029. Bool ok=false;
  1030. Lizard_streamDecode_t lizard; if(Lizard_setStreamDecode(&lizard, null, 0))
  1031. {
  1032. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  1033. Int cipher_offset=dest.posCipher();
  1034. Long start=dest.pos();
  1035. Ptr mem=dest.memFast();
  1036. Byte s[LIZARD_COMPRESSBOUND(LIZARD_BUF_SIZE)], d[LIZARD_BUF_SIZE*2]; Int d_pos=0;
  1037. Bool direct=MemDecompress(dest, decompressed_size);
  1038. for(; !src.end(); )
  1039. {
  1040. UInt chunk=src.decUIntV()+1; if(chunk>SIZE(s))goto error;
  1041. if(!src.getFast(s, chunk))goto error; // need exactly 'chunk' amount
  1042. if(direct)
  1043. {
  1044. auto size=Lizard_decompress_safe_continue(&lizard, (char*)s, (char*)dest.memFast(), chunk, dest.left()); if(size<0)goto error;
  1045. if(callback)callback->data(dest.memFast(), size);
  1046. if(!dest.skip(size))goto error; // can't use 'MemWrote' and 'Cipher' here because LIZARD may still access previously decompressed data
  1047. }else
  1048. {
  1049. if(d_pos>SIZE(d)-LIZARD_BUF_SIZE)d_pos=0; // if writing will exceed buffer size (this assumes that up to LIZARD_BUF_SIZE can be written at one time)
  1050. auto size=Lizard_decompress_safe_continue(&lizard, (char*)s, (char*)d+d_pos, chunk, SIZE(d)-d_pos); if(size<0)goto error;
  1051. if(callback)callback->data(d+d_pos, size);
  1052. if(!dest.put(d+d_pos, size))goto error; d_pos+=size;
  1053. }
  1054. }
  1055. {
  1056. Long wrote=dest.pos()-start;
  1057. if(direct)MemFinished(dest, mem, wrote, cipher_offset);
  1058. if(src.ok() && wrote==decompressed_size)ok=true;
  1059. }
  1060. error:
  1061. src.unlimit(temp_size, temp_offset);
  1062. }
  1063. return ok;
  1064. }
  1065. /******************************************************************************/
  1066. // keep as separate functions, because both of them use a lot of stack memory which could crash if combined together
  1067. static Bool LIZARDCompress(File &src, File &dest, Int compression_level, DataCallback *callback)
  1068. {
  1069. return (src.left()<=LIZARD_BUF_SIZE) ? LIZARDCompressMem (src, dest, compression_level, callback) // for small files prefer in-memory compression to avoid writing chunk sizes !! once placed here, don't change in the future because will break compatibility !!
  1070. : LIZARDCompressStream(src, dest, compression_level, callback);
  1071. }
  1072. static Bool LIZARDDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  1073. {
  1074. return (decompressed_size<=LIZARD_BUF_SIZE) ? LIZARDDecompressMem (src, dest, compressed_size, decompressed_size, callback) // for small files prefer in-memory compression to avoid writing chunk sizes !! once placed here, don't change in the future because will break compatibility !!
  1075. : LIZARDDecompressStream(src, dest, compressed_size, decompressed_size, callback);
  1076. }
  1077. #endif
  1078. /******************************************************************************/
  1079. // ZSTD
  1080. #if SUPPORT_ZSTD
  1081. ASSERT(ZSTD_BLOCKSIZE_MAX==128*1024); // this value needs to be constant
  1082. static ZSTD_customMem ZSTDMem={CompressAlloc, CompressFree, null};
  1083. static UIntPtr ZSTDSize(UIntPtr size) {return ZSTD_compressBound(size);}
  1084. static UInt ZSTDDictSizeLog2(Long size)
  1085. {
  1086. return (size<0) ? ZSTD_WINDOWLOG_MAX_32 // if the size is unknown, then use max possible dict size
  1087. : Mid(Log2Ceil(Unsigned(size)), ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX_32); // always limit to ZSTD_WINDOWLOG_MAX_32 ignoring ZSTD_WINDOWLOG_MAX_64 to keep consistency for both platforms
  1088. }
  1089. /******************************************************************************
  1090. static Bool ZSTDCompressFrame(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size, Int compression_level) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, false on fail
  1091. {
  1092. auto size=ZSTD_compress(dest, dest_size, src, src_size, Mid(compression_level, 1, ZSTD_maxCLevel()));
  1093. if(!ZSTD_isError(size)){dest_size=size; return true;}
  1094. return false;
  1095. }
  1096. static Bool ZSTDDecompressFrame(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  1097. {
  1098. auto size=ZSTD_decompress(dest, dest_size, src, src_size);
  1099. if(!ZSTD_isError(size)){dest_size=size; return true;}
  1100. return false;
  1101. }
  1102. /******************************************************************************
  1103. NOINLINE static Bool ZSTDCompressMemFrame(File &src, File &dest, Int compression_level)
  1104. {
  1105. Memt<Byte> s, d;
  1106. s.setNum(src.left()); if(!src.getFast(s.data(), s.elms()))return false;
  1107. d.setNum(ZSTDSize(s.elms())); UIntPtr dest_size=d.elms();
  1108. if(ZSTDCompressFrame(s.data(), s.elms(), d.data(), dest_size, compression_level))
  1109. {
  1110. s.del(); // delete 's' first to release memory
  1111. return dest.put(d.data(), dest_size);
  1112. }
  1113. return false;
  1114. }
  1115. NOINLINE static Bool ZSTDDecompressMemFrame(File &src, File &dest, Long compressed_size, Long decompressed_size)
  1116. {
  1117. Memt<Byte> s;
  1118. s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))return false;
  1119. if(MemDecompress(dest, decompressed_size))
  1120. {
  1121. UIntPtr dest_size=decompressed_size;
  1122. if(ZSTDDecompressFrame(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)return MemWrote(dest, dest_size);
  1123. }else
  1124. {
  1125. Memt<Byte> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  1126. if(ZSTDDecompressFrame(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  1127. {
  1128. s.del(); // delete 's' first to release memory
  1129. return dest.put(d.data(), dest_size);
  1130. }
  1131. }
  1132. return false;
  1133. }
  1134. /******************************************************************************
  1135. NOINLINE static Bool ZSTDCompressFrame(File &src, File &dest, Int compression_level)
  1136. {
  1137. Bool ok=false;
  1138. if(ZSTD_CCtx *ctx=ZSTD_createCCtx_advanced(ZSTDMem))
  1139. {
  1140. ZSTD_parameters params=ZSTD_getParams(Mid(compression_level, 1, ZSTD_maxCLevel()), src.left(), 0);
  1141. #if 0 // don't write anything to speedup processing
  1142. params.fParams.contentSizeFlag=true;
  1143. params.fParams. noDictIDFlag=true;
  1144. params.fParams. checksumFlag=true;
  1145. #endif
  1146. if(!ZSTD_isError(ZSTD_compressBegin_advanced(ctx, null, 0, params, src.left())))
  1147. {
  1148. // sizes for 'window_size', 'block_size', 's', 'd' were taken from "zstd" tutorial, "zbuff_compress.c" file, "ZBUFF_compressInit_advanced" function
  1149. C Int window_size=1<<params.cParams.windowLog, block_size=Min(window_size, ZSTD_BLOCKSIZE_MAX);
  1150. Memt<Byte> s, d; s.setNum(window_size+block_size); d.setNum(ZSTDSize(block_size)+1); Int s_pos=0;
  1151. for(; !src.end(); )
  1152. {
  1153. Int read=Min(ZSTD_BLOCKSIZE_MAX, Min(s.elms(), src.left())); // ZSTD_BLOCKSIZE_MAX taken from 'ZBUFF_recommendedCInSize' (without this, 'ZSTD_compressContinue' may fail with 'dest' too small error)
  1154. if(s_pos>s.elms()-read)s_pos=0; // if reading will exceed buffer size
  1155. read=src.getReturnSize(&s[s_pos], read); if(read<=0)goto error;
  1156. auto size=ZSTD_compressContinue(ctx, d.data(), d.elms(), &s[s_pos], read); if(ZSTD_isError(size))goto error;
  1157. if(!dest.put(d.data(), size))goto error;
  1158. s_pos+=read;
  1159. }
  1160. auto size=ZSTD_compressEnd(ctx, d.data(), d.elms()); if(ZSTD_isError(size))goto error;
  1161. if(dest.put(d.data(), size))ok=true;
  1162. }
  1163. error:
  1164. ZSTD_freeCCtx(ctx);
  1165. }
  1166. return ok;
  1167. }
  1168. NOINLINE static Bool ZSTDDecompressFrame(File &src, File &dest, Long compressed_size, Long decompressed_size)
  1169. {
  1170. Bool ok=false;
  1171. if(ZSTD_DCtx *ctx=ZSTD_createDCtx_advanced(ZSTDMem))
  1172. {
  1173. ZSTD_decompressBegin(ctx);
  1174. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  1175. Byte header[ZSTD_frameHeaderSize_max];
  1176. Long pos=src.pos();
  1177. Int read=src.getReturnSize(header, SIZE(header));
  1178. src.pos(pos);
  1179. ZSTD_frameParams frame; if(!ZSTD_getFrameParams(&frame, header, read))
  1180. {
  1181. Int cipher_offset=dest.posCipher();
  1182. Long start=dest.pos();
  1183. Ptr mem=dest.memFast();
  1184. // sizes for 'block_size', 's', 'd' were taken from "zstd" tutorial, "zbuff_decompress.c" file, "ZBUFF_decompressContinue" function
  1185. C auto block_size=Min(frame.windowSize, ZSTD_BLOCKSIZE_MAX);
  1186. Memt<Byte> s, d; s.setNum(block_size); Int d_pos=0;
  1187. Bool direct=MemDecompress(dest, decompressed_size); if(!direct)d.setNum(frame.windowSize+block_size);
  1188. for(;;)
  1189. {
  1190. auto size=ZSTD_nextSrcSizeToDecompress(ctx); if(!size){if(dest.pos()-start==decompressed_size)ok=true; break;} if(ZSTD_isError(size) || size>s.elms())break;
  1191. if(!src.getFast(s.data(), size))break; // need exactly 'size' amount
  1192. if(direct)
  1193. {
  1194. size=ZSTD_decompressContinue(ctx, dest.memFast(), dest.left(), s.data(), size); if(ZSTD_isError(size))break;
  1195. if(!dest.skip(size))break; // can't use 'MemWrote' and 'Cipher' here because ZSTD may still access previously decompressed data
  1196. }else
  1197. {
  1198. if(d_pos>d.elms()-block_size)d_pos=0; // if decompressing will exceed buffer size
  1199. size=ZSTD_decompressContinue(ctx, &d[d_pos], d.elms()-d_pos, s.data(), size); if(ZSTD_isError(size))break;
  1200. if(size>0){if(!dest.put(&d[d_pos], size))break; d_pos+=size;}
  1201. }
  1202. }
  1203. if(direct)MemFinished(dest, mem, dest.pos()-start, cipher_offset);
  1204. }
  1205. src.unlimit(temp_size, temp_offset);
  1206. ZSTD_freeDCtx(ctx);
  1207. }
  1208. return ok;
  1209. }
  1210. /******************************************************************************/
  1211. NOINLINE static Bool ZSTDCompress(File &src, File &dest, Int compression_level, DataCallback *callback)
  1212. {
  1213. Bool ok=false;
  1214. if(ZSTD_CCtx *ctx=ZSTD_createCCtx_advanced(ZSTDMem))
  1215. {
  1216. ZSTD_parameters params=ZSTD_getParams(Mid(compression_level, 1, ZSTD_maxCLevel()), src.left(), 0);
  1217. #if 0 // don't write anything to speedup processing (for block-based this is most likely ignored)
  1218. params.fParams.contentSizeFlag=true;
  1219. params.fParams. noDictIDFlag=true;
  1220. params.fParams. checksumFlag=true;
  1221. #endif
  1222. #if ZSTD_WINDOWLOG_SAVE
  1223. dest.putByte(params.cParams.windowLog);
  1224. #else
  1225. params.cParams.windowLog=ZSTDDictSizeLog2(src.left());
  1226. #endif
  1227. if(!ZSTD_isError(ZSTD_compressBegin_advanced(ctx, null, 0, params, src.left())))
  1228. {
  1229. Bool single=(src.left()<=ZSTD_BLOCKSIZE_MAX);
  1230. C Int window_size=1<<params.cParams.windowLog;
  1231. Memt<Byte> s, d; Int s_pos=0;
  1232. s.setNum( Min(window_size+ZSTD_BLOCKSIZE_MAX, src.left()) );
  1233. d.setNum(ZSTDSize(Min( ZSTD_BLOCKSIZE_MAX, src.left())));
  1234. for(; !src.end(); )
  1235. {
  1236. Int read=Min(ZSTD_BLOCKSIZE_MAX, Min(s.elms(), src.left()));
  1237. if(s_pos>s.elms()-read)s_pos=0; // if reading will exceed buffer size
  1238. read=src.getReturnSize(&s[s_pos], read); if(read<=0)goto error;
  1239. if(callback)callback->data(&s[s_pos], read);
  1240. auto size=ZSTD_compressBlock(ctx, d.data(), d.elms(), &s[s_pos], read); if(ZSTD_isError(size))goto error; // 'ZSTD_compressBlock' returns 0 if failed to compress
  1241. dest.cmpUIntV(single ? (size>0) : size); // for single we store (compressed ? 1 : 0), for multi we store (compressed ? size : 0)
  1242. if(size>0){if(!dest.put( d.data(), size))goto error;} // compressed
  1243. else {if(!dest.put(&s[s_pos], read))goto error;} // failed to compress
  1244. s_pos+=read;
  1245. }
  1246. if(dest.ok())ok=true;
  1247. }
  1248. error:
  1249. ZSTD_freeCCtx(ctx);
  1250. }
  1251. return ok;
  1252. }
  1253. NOINLINE static Bool ZSTDDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  1254. {
  1255. Bool ok=false;
  1256. if(ZSTD_DCtx *ctx=ZSTD_createDCtx_advanced(ZSTDMem))
  1257. {
  1258. ZSTD_decompressBegin(ctx);
  1259. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  1260. Int cipher_offset=dest.posCipher();
  1261. Long start=dest.pos();
  1262. Ptr mem=dest.memFast();
  1263. Bool single=(decompressed_size<=ZSTD_BLOCKSIZE_MAX);
  1264. #if ZSTD_WINDOWLOG_SAVE
  1265. C Int window_size=1<<src.getByte();
  1266. #else
  1267. C Int window_size=1<<ZSTDDictSizeLog2(decompressed_size);
  1268. #endif
  1269. Memt<Byte> s, d; s.setNum(Min((Int)ZSTDSize(ZSTD_BLOCKSIZE_MAX), compressed_size)); Int d_pos=0;
  1270. Bool direct=MemDecompress(dest, decompressed_size); if(!direct)d.setNum(window_size+ZSTD_BLOCKSIZE_MAX); // here don't apply "Min(decompressed_size,", so "if(d_pos>d.elms()-ZSTD_BLOCKSIZE_MAX)" doesn't wrap the 'd_pos' to 0
  1271. for(; !src.end(); )
  1272. {
  1273. UInt chunk; src.decUIntV(chunk);
  1274. if( chunk) // compressed
  1275. {
  1276. if(single)
  1277. {
  1278. if(chunk>1)goto error; // for single 'chunk' can be either 0 or 1
  1279. chunk=src.left();
  1280. }
  1281. if(chunk>s.elms())goto error;
  1282. if(!src.getFast(s.data(), chunk))goto error; // need exactly 'chunk' amount
  1283. if(direct)
  1284. {
  1285. auto size=ZSTD_decompressBlock(ctx, dest.memFast(), dest.left(), s.data(), chunk); if(ZSTD_isError(size))goto error;
  1286. if(callback)callback->data(dest.memFast(), size);
  1287. if(!dest.skip(size))goto error; // can't use 'MemWrote' and 'Cipher' here because ZSTD may still access previously decompressed data
  1288. }else
  1289. {
  1290. if(d_pos>d.elms()-ZSTD_BLOCKSIZE_MAX)d_pos=0; // if decompressing will exceed buffer size
  1291. auto size=ZSTD_decompressBlock(ctx, &d[d_pos], d.elms()-d_pos, s.data(), chunk); if(ZSTD_isError(size))goto error;
  1292. if(callback)callback->data(&d[d_pos], size);
  1293. if(!dest.put(&d[d_pos], size))goto error; d_pos+=size;
  1294. }
  1295. }else // un-compressed
  1296. {
  1297. Int size=Min(ZSTD_BLOCKSIZE_MAX, src.left()); // for 'single' this will have the same value, so we can ignore it
  1298. if(direct)
  1299. {
  1300. Ptr mem=dest.memFast();
  1301. if(!src.getFast(mem, size)
  1302. || ZSTD_isError(ZSTD_insertBlock(ctx, mem, size))
  1303. || !dest.skip(size))goto error; // can't use 'MemWrote' and 'Cipher' here because ZSTD may still access previously decompressed data
  1304. if(callback)callback->data(mem, size);
  1305. }else
  1306. {
  1307. if(d_pos>d.elms()-size) // if reading will exceed buffer size
  1308. {
  1309. if(size>d.elms())goto error; // if reading doesn't fit in buffer size
  1310. d_pos=0;
  1311. }
  1312. if(!src.getFast(&d[d_pos], size)
  1313. || ZSTD_isError(ZSTD_insertBlock(ctx, &d[d_pos], size))
  1314. || !dest.put(&d[d_pos], size))goto error;
  1315. if(callback)callback->data(&d[d_pos], size);
  1316. d_pos+=size;
  1317. }
  1318. }
  1319. }
  1320. {
  1321. Long wrote=dest.pos()-start;
  1322. if(direct)MemFinished(dest, mem, wrote, cipher_offset);
  1323. if(src.ok() && wrote==decompressed_size)ok=true;
  1324. }
  1325. error:
  1326. src.unlimit(temp_size, temp_offset);
  1327. ZSTD_freeDCtx(ctx);
  1328. }
  1329. return ok;
  1330. }
  1331. #endif
  1332. /******************************************************************************/
  1333. // BROTLI
  1334. /******************************************************************************/
  1335. #if SUPPORT_BROTLI
  1336. static UIntPtr BrotliSize(UIntPtr size) {return BrotliEncoderMaxCompressedSize(size);}
  1337. static UInt BrotliDictSizeLog2(Long size)
  1338. {
  1339. return (size<0) ? kBrotliMaxWindowBits // if the size is unknown, then use max possible dict size
  1340. : Mid(Log2Ceil(Unsigned(size)), kBrotliMinWindowBits, kBrotliMaxWindowBits);
  1341. }
  1342. static Bool BrotliCompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size, Int compression_level) // compress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to compressed size, 'compression_level'=(0..9) (0=fastest/worst, 9=slowest/best), false on fail
  1343. {
  1344. size_t size=dest_size; Bool ok=(BrotliEncoderCompress(Mid(compression_level, 0, 11), BrotliDictSizeLog2(src_size), BROTLI_DEFAULT_MODE, src_size, (uint8_t*)src, &size, (uint8_t*)dest)==1);
  1345. dest_size= size; return ok;
  1346. }
  1347. static Bool BrotliDecompress(CPtr src, UIntPtr src_size, Ptr dest, UIntPtr &dest_size) // decompress data, 'src'=source buffer, 'src_size'=source size, 'dest'=destination buffer, 'dest_size'=destination size, before calling it should be set to maximum 'dest' buffer capacity, after calling it'll be set to decompressed size, false on fail
  1348. {
  1349. size_t size=dest_size; Bool ok=(BrotliDecompressBuffer(src_size, (uint8_t*)src, &size, (uint8_t*)dest)==BROTLI_RESULT_SUCCESS);
  1350. dest_size= size; return ok;
  1351. }
  1352. /******************************************************************************/
  1353. NOINLINE static Bool BrotliCompressMem(File &src, File &dest, Int compression_level)
  1354. {
  1355. Memt<Byte> s, d;
  1356. s.setNum(src.left()); if(!src.getFast(s.data(), s.elms()))return false;
  1357. d.setNum(BrotliSize(s.elms())); UIntPtr dest_size=d.elms();
  1358. if(BrotliCompress(s.data(), s.elms(), d.data(), dest_size, compression_level))
  1359. {
  1360. s.del(); // delete 's' first to release memory
  1361. return dest.put(d.data(), dest_size);
  1362. }
  1363. return false;
  1364. }
  1365. NOINLINE static Bool BrotliDecompressMem(File &src, File &dest, Long compressed_size, Long decompressed_size)
  1366. {
  1367. Memt<Byte> s;
  1368. s.setNum(compressed_size); if(!src.getFast(s.data(), s.elms()))return false;
  1369. if(MemDecompress(dest, decompressed_size))
  1370. {
  1371. UIntPtr dest_size=decompressed_size;
  1372. if(BrotliDecompress(s.data(), s.elms(), dest.memFast(), dest_size) && dest_size==decompressed_size)return MemWrote(dest, dest_size);
  1373. }else
  1374. {
  1375. Memt<Byte> d; d.setNum(decompressed_size); UIntPtr dest_size=d.elms();
  1376. if(BrotliDecompress(s.data(), s.elms(), d.data(), dest_size) && dest_size==decompressed_size)
  1377. {
  1378. s.del(); // delete 's' first to release memory
  1379. return dest.put(d.data(), dest_size);
  1380. }
  1381. }
  1382. return false;
  1383. }
  1384. /******************************************************************************/
  1385. NOINLINE static Bool BrotliCompress(File &src, File &dest, Int compression_level)
  1386. {
  1387. Bool ok=false;
  1388. if(BrotliEncoderState *brotli=BrotliEncoderCreateInstance(CompressAlloc, CompressFree, null))
  1389. {
  1390. BrotliEncoderSetParameter(brotli, BROTLI_PARAM_MODE , BROTLI_MODE_GENERIC);
  1391. BrotliEncoderSetParameter(brotli, BROTLI_PARAM_QUALITY, Mid(compression_level, 0, 11));
  1392. BrotliEncoderSetParameter(brotli, BROTLI_PARAM_LGWIN , BrotliDictSizeLog2(src.left()));
  1393. //BrotliEncoderSetParameter(brotli, BROTLI_PARAM_LGBLOCK, lgblock);
  1394. Byte in[BUF_SIZE], out[BUF_SIZE];
  1395. for(;;)
  1396. {
  1397. Int read=src.getReturnSize(in, SIZE(in)); // always proceed even if 'read'==0 in case of empty files or if we've encountered end of file
  1398. size_t avail_in=read, avail_out;
  1399. C uint8_t *next_in=in ;
  1400. do
  1401. {
  1402. avail_out=SIZE(out);
  1403. uint8_t *next_out= out ;
  1404. if(!BrotliEncoderCompressStream(brotli, src.end() ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, &avail_in, &next_in, &avail_out, &next_out, null))goto error;
  1405. if(!dest.put(out, SIZE(out)-avail_out))goto error;
  1406. }while(avail_out==0);
  1407. if(avail_in )goto error; // if there's still some input left, then it failed
  1408. if(src.end()){ok=true; break;}
  1409. if(read<=0 )goto error; // if not end of 'src' but failed to read anything, then it failed
  1410. }
  1411. error:
  1412. BrotliEncoderDestroyInstance(brotli);
  1413. }
  1414. return ok;
  1415. }
  1416. NOINLINE static Bool BrotliDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size)
  1417. {
  1418. Bool ok=false;
  1419. if(BrotliState *brotli=BrotliCreateState(CompressAlloc, CompressFree, null))
  1420. {
  1421. Long start=dest.pos();
  1422. Byte in[BUF_SIZE], out[BUF_SIZE];
  1423. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  1424. Bool direct=MemDecompress(dest, decompressed_size);
  1425. for(;;)
  1426. {
  1427. Int read=src.getReturnSize(in, SIZE(in)); if(read<=0)goto error;
  1428. size_t avail_in=read, avail_out;
  1429. C uint8_t *next_in=in ; uint8_t *next_out;
  1430. BrotliResult result;
  1431. do{
  1432. if(direct)
  1433. {
  1434. avail_out= dest.left ();
  1435. next_out=(Byte*)dest.memFast();
  1436. }else
  1437. {
  1438. avail_out=SIZE(out);
  1439. next_out= out ;
  1440. }
  1441. result=BrotliDecompressStream(&avail_in, &next_in, &avail_out, &next_out, null, brotli);
  1442. if(result==BROTLI_RESULT_ERROR)goto error;
  1443. if(direct ? !MemWrote(dest, dest.left()-avail_out)
  1444. : !dest.put(out , SIZE(out) -avail_out))goto error;
  1445. }while(result==BROTLI_RESULT_NEEDS_MORE_OUTPUT);
  1446. if(result==BROTLI_RESULT_SUCCESS){if(dest.pos()-start==decompressed_size)ok=true; break;}
  1447. }
  1448. error:
  1449. src.unlimit(temp_size, temp_offset);
  1450. BrotliDestroyState(brotli);
  1451. }
  1452. return ok;
  1453. }
  1454. #endif
  1455. /******************************************************************************/
  1456. // LZHAM
  1457. /******************************************************************************/
  1458. #if SUPPORT_LZHAM
  1459. static UInt LZHAMDictSizeLog2(Long size) // !! don't change this function, because the compression/decompression values must match, so all compressed data from the past must use the same values for the decompressor !!
  1460. {
  1461. return (size<0) ? LZHAM_MAX_DICT_SIZE_LOG2_X86 // if the size is unknown, then use max possible dict size
  1462. : Mid(Log2Ceil(Unsigned(size)), LZHAM_MIN_DICT_SIZE_LOG2, LZHAM_MAX_DICT_SIZE_LOG2_X86); // always limit to LZHAM_MAX_DICT_SIZE_LOG2_X86 ignoring LZHAM_MAX_DICT_SIZE_LOG2_X64 to keep consistency for both platforms
  1463. }
  1464. static UInt LZHAMDictSize(Long size) {return 1<<LZHAMDictSizeLog2(size);}
  1465. NOINLINE static Bool LZHAMCompress(File &src, File &dest, Int compression_level, Bool multi_threaded, DataCallback *callback)
  1466. {
  1467. using namespace lzham;
  1468. Bool ok=false;
  1469. lzham_compress_params comp_params; Zero(comp_params);
  1470. comp_params.m_struct_size =SIZE(comp_params);
  1471. comp_params.m_dict_size_log2=LZHAMDictSizeLog2(src.left());
  1472. comp_params.m_level =(lzham_compress_level)Mid(compression_level, LZHAM_COMP_LEVEL_FASTEST, LZHAM_TOTAL_COMP_LEVELS-1);
  1473. //if(compression_level>comp_params.m_level)comp_params.m_compress_flags|=LZHAM_COMP_FLAG_EXTREME_PARSING; // don't enable this as it's super slow (30x slower) and without significant improvement
  1474. comp_params.m_compress_flags|=LZHAM_COMP_FLAG_DETERMINISTIC_PARSING; // make sure that compressed data is always the same
  1475. comp_params.m_max_helper_threads=(multi_threaded ? -1 : 0);
  1476. if(lzham_compress_state_ptr cs=lzham_lib_compress_init(&comp_params))
  1477. {
  1478. Byte in[BUF_SIZE], out[BUF_SIZE], *next_in; Int avail_in=0;
  1479. for(;;)
  1480. {
  1481. if(!avail_in)
  1482. {
  1483. avail_in=src.getReturnSize(in, SIZE(in)); // always proceed even if 'avail_in'==0 in case of empty files or if we've encountered end of file
  1484. next_in=in;
  1485. if(callback)callback->data(in, avail_in);
  1486. }
  1487. lzham_compress_status_t status;
  1488. do{
  1489. size_t in_size=avail_in, out_size=SIZE(out);
  1490. status=lzham_lib_compress2(cs, next_in, &in_size, out, &out_size, src.end() ? LZHAM_FINISH : LZHAM_NO_FLUSH);
  1491. if(status>=LZHAM_COMP_STATUS_FIRST_FAILURE_CODE)goto error;
  1492. if(!dest.put(out, out_size))goto error;
  1493. next_in+=in_size;
  1494. avail_in-=in_size;
  1495. }while(status==LZHAM_COMP_STATUS_HAS_MORE_OUTPUT);
  1496. if(!avail_in && status!=LZHAM_COMP_STATUS_NOT_FINISHED && src.end()){if(status==LZHAM_COMP_STATUS_SUCCESS)ok=true; break;}
  1497. }
  1498. error:
  1499. lzham_lib_compress_deinit(cs);
  1500. }
  1501. return ok;
  1502. }
  1503. NOINLINE static Bool LZHAMDecompress(File &src, File &dest, Long compressed_size, Long decompressed_size, DataCallback *callback)
  1504. {
  1505. using namespace lzham;
  1506. Bool ok=false,
  1507. direct=MemDecompress(dest, decompressed_size);
  1508. lzham_decompress_params comp_params; Zero(comp_params);
  1509. comp_params.m_struct_size =SIZE(comp_params);
  1510. comp_params.m_dict_size_log2=LZHAMDictSizeLog2(decompressed_size);
  1511. if(direct)comp_params.m_decompress_flags=LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED;
  1512. if(lzham_decompress_state_ptr cs=lzham_lib_decompress_init(&comp_params))
  1513. {
  1514. Long start=dest.pos();
  1515. ULong temp_size, temp_offset; src.limit(temp_size, temp_offset, compressed_size);
  1516. Byte in[BUF_SIZE], out[BUF_SIZE], *next_in; Int avail_in=0;
  1517. for(;;)
  1518. {
  1519. if(!avail_in)
  1520. {
  1521. avail_in=src.getReturnSize(in, SIZE(in)); if(avail_in<=0)break;
  1522. next_in=in;
  1523. }
  1524. lzham_decompress_status_t status;
  1525. do{
  1526. size_t in_size=avail_in, out_size;
  1527. Ptr out_buf;
  1528. if(direct)
  1529. {
  1530. out_buf =dest.memFast();
  1531. out_size=dest.left ();
  1532. }else
  1533. {
  1534. out_buf = out ;
  1535. out_size=SIZE(out);
  1536. }
  1537. status=lzham_lib_decompress(cs, next_in, &in_size, (Byte*)out_buf, &out_size, src.end());
  1538. if(status>=LZHAM_DECOMP_STATUS_FIRST_FAILURE_CODE)goto error;
  1539. if(callback)callback->data(out_buf, out_size);
  1540. if(direct ? !MemWrote(dest, out_size) : !dest.put(out, out_size))goto error;
  1541. next_in+=in_size;
  1542. avail_in-=in_size;
  1543. }while(status==LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT);
  1544. if(!avail_in && status!=LZHAM_DECOMP_STATUS_NOT_FINISHED && src.end()){if(status==LZHAM_DECOMP_STATUS_SUCCESS && dest.pos()-start==decompressed_size)ok=true; break;}
  1545. }
  1546. error:
  1547. src.unlimit(temp_size, temp_offset);
  1548. lzham_lib_decompress_deinit(cs);
  1549. }
  1550. return ok;
  1551. }
  1552. #endif
  1553. /******************************************************************************/
  1554. // CUSTOM
  1555. /******************************************************************************/
  1556. Bool CompressRaw(File &src, File &dest, COMPRESS_TYPE type, Int compression_level, Bool multi_threaded, DataCallback *callback)
  1557. {
  1558. switch(type)
  1559. {
  1560. case COMPRESS_NONE : return callback ? src.copy(dest, *callback) : src.copy(dest);
  1561. #if SUPPORT_ZLIB
  1562. case COMPRESS_ZLIB : return ZLIBCompress(src, dest, compression_level , callback);
  1563. #endif
  1564. #if SUPPORT_LZMA
  1565. case COMPRESS_LZMA : return LZMACompress(src, dest, compression_level, multi_threaded, callback);
  1566. #endif
  1567. #if SUPPORT_SNAPPY
  1568. case COMPRESS_SNAPPY: return SNAPPYCompress(src, dest , callback);
  1569. #endif
  1570. #if SUPPORT_RLE
  1571. case COMPRESS_RLE : return RLECompress(src, dest , callback);
  1572. #endif
  1573. #if SUPPORT_LZ4
  1574. case COMPRESS_LZ4 : return LZ4Compress(src, dest, compression_level , callback);
  1575. #endif
  1576. #if SUPPORT_LIZARD
  1577. case COMPRESS_LIZARD: return LIZARDCompress(src, dest, compression_level , callback);
  1578. #endif
  1579. #if SUPPORT_LZHAM
  1580. case COMPRESS_LZHAM : return LZHAMCompress(src, dest, compression_level, multi_threaded, callback);
  1581. #endif
  1582. #if SUPPORT_BROTLI
  1583. case COMPRESS_BROTLI: return BrotliCompress(src, dest, compression_level , callback);
  1584. #endif
  1585. #if SUPPORT_ZSTD
  1586. case COMPRESS_ZSTD : return ZSTDCompress(src, dest, compression_level , callback);
  1587. #endif
  1588. default : return false;
  1589. }
  1590. }
  1591. Bool DecompressRaw(File &src, File &dest, COMPRESS_TYPE type, ULong compressed_size, ULong decompressed_size, Bool memory, DataCallback *callback)
  1592. {
  1593. if(memory)dest.writeMemFixed(decompressed_size);
  1594. switch(type)
  1595. {
  1596. case COMPRESS_NONE : return callback ? src.copy(dest, *callback, decompressed_size) : src.copy(dest, decompressed_size);
  1597. #if SUPPORT_ZLIB
  1598. case COMPRESS_ZLIB : return ZLIBDecompress(src, dest, compressed_size, decompressed_size, callback);
  1599. #endif
  1600. #if SUPPORT_LZMA
  1601. case COMPRESS_LZMA : return LZMADecompress(src, dest, compressed_size, decompressed_size, callback);
  1602. #endif
  1603. #if SUPPORT_SNAPPY
  1604. case COMPRESS_SNAPPY: return SNAPPYDecompress(src, dest, compressed_size, decompressed_size, callback);
  1605. #endif
  1606. #if SUPPORT_RLE
  1607. case COMPRESS_RLE : return RLEDecompress(src, dest, compressed_size, decompressed_size, callback);
  1608. #endif
  1609. #if SUPPORT_LZ4
  1610. case COMPRESS_LZ4 : return LZ4Decompress(src, dest, compressed_size, decompressed_size, callback);
  1611. #endif
  1612. #if SUPPORT_LIZARD
  1613. case COMPRESS_LIZARD: return LIZARDDecompress(src, dest, compressed_size, decompressed_size, callback);
  1614. #endif
  1615. #if SUPPORT_LZHAM
  1616. case COMPRESS_LZHAM : return LZHAMDecompress(src, dest, compressed_size, decompressed_size, callback);
  1617. #endif
  1618. #if SUPPORT_BROTLI
  1619. case COMPRESS_BROTLI: return BrotliDecompress(src, dest, compressed_size, decompressed_size, callback);
  1620. #endif
  1621. #if SUPPORT_ZSTD
  1622. case COMPRESS_ZSTD : return ZSTDDecompress(src, dest, compressed_size, decompressed_size, callback);
  1623. #endif
  1624. default : return false;
  1625. }
  1626. }
  1627. /******************************************************************************/
  1628. Bool Compress(File &src, File &dest, COMPRESS_TYPE type, Int compression_level, Bool multi_threaded, DataCallback *callback)
  1629. {
  1630. Long uncompressed_size=src.left(), dest_start=dest.pos();
  1631. if( uncompressed_size<MIN_COMPRESSABLE_SIZE+1)type=COMPRESS_NONE; // add +1 because compressed types require storing 'compressed_size' which uses memory as well
  1632. start:
  1633. // save header
  1634. dest.putByte (type+1 ); // compression type +1, because 0 was old version=0
  1635. dest.cmpULongV(uncompressed_size); // decompressed size
  1636. Int compressed_size_bytes;
  1637. ULong compressed_size, compressed_size_pos; if(type) // make room for compressed size to be written later, because we don't know it yet
  1638. {
  1639. compressed_size_bytes=ByteHi(Unsigned(uncompressed_size)); // estimate how many bytes we need
  1640. compressed_size_pos =dest.pos();
  1641. dest.put(null, compressed_size_bytes); // write zeros
  1642. }
  1643. // compress
  1644. if(!CompressRaw(src, dest, type, compression_level, multi_threaded, callback))return false;
  1645. if(type) // write compressed size
  1646. {
  1647. Long cur_pos=dest.pos();
  1648. compressed_size=cur_pos-compressed_size_pos-compressed_size_bytes;
  1649. if(ByteHi(compressed_size)>compressed_size_bytes) // if we need more than what was reserved, then fallback into COMPRESS_NONE
  1650. {
  1651. if(!src .skip(-uncompressed_size))return false; // go back to what was read
  1652. if(!dest.pos ( dest_start ))return false; // go back to what was written
  1653. if(!dest.trim( ))return false; // remove everything that was written
  1654. type=COMPRESS_NONE;
  1655. #if 1
  1656. callback=null; // we can just stop calling the callback, because most likely it's used for calculating the hash, so if we've already calculated it, no reason to calculate again
  1657. #else
  1658. if(callback)callback->reset(); // reset callback because we will write data all over again
  1659. #endif
  1660. goto start;
  1661. }
  1662. if(!dest.pos( compressed_size_pos ))return false;
  1663. if(!dest.put(&compressed_size, compressed_size_bytes))return false;
  1664. if(!dest.pos( cur_pos ))return false;
  1665. }
  1666. return dest.ok();
  1667. }
  1668. /******************************************************************************/
  1669. Bool Decompress(File &src, File &dest, Bool memory, DataCallback *callback)
  1670. {
  1671. switch(UInt type=src.decUIntV())
  1672. {
  1673. default:
  1674. {
  1675. type--;
  1676. ULong compressed_size, decompressed_size=src.decULongV();
  1677. if(type){compressed_size=0; src.getFast(&compressed_size, ByteHi(decompressed_size));}
  1678. else compressed_size=decompressed_size;
  1679. if(src.ok())return DecompressRaw(src, dest, COMPRESS_TYPE(type), compressed_size, decompressed_size, memory, callback);
  1680. }break;
  1681. case 0:
  1682. {
  1683. COMPRESS_TYPE type=COMPRESS_TYPE(src.getByte ());
  1684. UInt decompressed_size= src.decUIntV() ;
  1685. UInt compressed_size= src.getUInt () ;
  1686. if(src.ok())
  1687. {
  1688. if(memory)dest.writeMemFixed(decompressed_size);
  1689. switch(type)
  1690. {
  1691. case COMPRESS_NONE : return callback ? src.copy(dest, *callback, decompressed_size) : src.copy(dest, decompressed_size);
  1692. #if SUPPORT_ZLIB
  1693. case COMPRESS_ZLIB : return ZLIBDecompress (src, dest, compressed_size, decompressed_size, callback);
  1694. #endif
  1695. #if SUPPORT_LZMA
  1696. case COMPRESS_LZMA : return LZMADecompress (src, dest, compressed_size, decompressed_size, callback);
  1697. #endif
  1698. #if SUPPORT_SNAPPY
  1699. case COMPRESS_SNAPPY: return SNAPPYDecompressMem(src, dest, compressed_size, decompressed_size, callback);
  1700. #endif
  1701. #if SUPPORT_RLE
  1702. case COMPRESS_RLE : return RLEDecompress (src, dest, compressed_size, decompressed_size, callback);
  1703. #endif
  1704. #if SUPPORT_LZ4
  1705. case COMPRESS_LZ4 : return LZ4DecompressMem(src, dest, compressed_size, decompressed_size, callback);
  1706. #endif
  1707. #if SUPPORT_LZHAM
  1708. case COMPRESS_LZHAM : return LZHAMDecompress (src, dest, compressed_size, decompressed_size, callback);
  1709. #endif
  1710. }
  1711. }
  1712. }break;
  1713. }
  1714. return false;
  1715. }
  1716. Bool DecompressHeader(File &src, COMPRESS_TYPE &type, ULong &compressed_size, ULong &decompressed_size)
  1717. {
  1718. switch(UInt ver=src.decUIntV())
  1719. {
  1720. default:
  1721. {
  1722. type=COMPRESS_TYPE(ver-1);
  1723. decompressed_size=src.decULongV();
  1724. if(type){compressed_size=0; src.getFast(&compressed_size, ByteHi(decompressed_size));}
  1725. else compressed_size=decompressed_size;
  1726. if(src.ok())return true;
  1727. }break;
  1728. case 0:
  1729. {
  1730. type=COMPRESS_TYPE(src.getByte ());
  1731. decompressed_size= src.decUIntV() ;
  1732. compressed_size= src.getUInt () ;
  1733. if(src.ok())return true;
  1734. }break;
  1735. }
  1736. return false;
  1737. }
  1738. /******************************************************************************/
  1739. Bool Compressable(C Str &ext)
  1740. {
  1741. if(ext=="jpg" || ext=="jpeg" || ext=="png" || ext=="webp" || ext=="bpg" || ext=="flif" // image
  1742. || ext=="mp3" || ext=="wma" || ext=="ogg" || ext=="flac" || ext=="m4a" || ext=="opus" || ext=="weba" // sound
  1743. || ext=="avi" || ext=="mpg" || ext=="mpeg" || ext=="mp4" || ext=="m4v" || ext=="mkv" || ext=="wmv" || ext=="rmvb" || ext=="divx" || ext=="ogm" || ext=="ogv" || ext=="theora" || ext=="webm" || ext=="vob" || ext=="flv" // video
  1744. || ext=="zip" || ext=="rar" || ext=="7z" || ext=="gz" || ext=="bz2" || ext=="tgz" || ext=="tbz" || ext=="xz" // archives ("tar" itself is not compressed)
  1745. )return false;
  1746. return true;
  1747. }
  1748. CChar8* CompressionName(COMPRESS_TYPE type)
  1749. {
  1750. switch(type)
  1751. {
  1752. case COMPRESS_NONE : return "None";
  1753. case COMPRESS_ZLIB : return "Zlib";
  1754. case COMPRESS_LZMA : return "LZMA";
  1755. case COMPRESS_SNAPPY: return "Snappy";
  1756. case COMPRESS_RLE : return "RLE";
  1757. case COMPRESS_LZ4 : return "LZ4";
  1758. case COMPRESS_LIZARD: return "Lizard";
  1759. case COMPRESS_LZHAM : return "LZHAM";
  1760. case COMPRESS_ZSTD : return "Zstd";
  1761. case COMPRESS_BROTLI: return "Brotli";
  1762. default : return null;
  1763. }
  1764. }
  1765. VecI2 CompressionLevels(COMPRESS_TYPE type)
  1766. {
  1767. switch(type)
  1768. {
  1769. default : return 0; // single compression level
  1770. case COMPRESS_ZLIB : return VecI2(1, 9); // 0=no compression
  1771. case COMPRESS_LZMA : return VecI2(0, 9);
  1772. #if SUPPORT_LZHAM
  1773. case COMPRESS_LZHAM : return VecI2(LZHAM_COMP_LEVEL_FASTEST, LZHAM_TOTAL_COMP_LEVELS-1);
  1774. #endif
  1775. #if SUPPORT_LZ4
  1776. case COMPRESS_LZ4 : return VecI2(0, LZ4HC_CLEVEL_MAX); // 0=LZ4, 1..LZ4HC_CLEVEL_MAX=LZ4_HC
  1777. #endif
  1778. #if SUPPORT_LIZARD
  1779. case COMPRESS_LIZARD: return VecI2(LIZARD_MIN_CLEVEL, LIZARD_MAX_CLEVEL);
  1780. #endif
  1781. case COMPRESS_BROTLI: return VecI2(0, 11); // taken from "enc/encode.h" header
  1782. #if SUPPORT_ZSTD
  1783. case COMPRESS_ZSTD : return VecI2(1, ZSTD_maxCLevel()); // 0=never used
  1784. #endif
  1785. }
  1786. }
  1787. UInt CompressionMemUsage(COMPRESS_TYPE type, Int compression_level, Long uncompressed_size)
  1788. {
  1789. switch(type)
  1790. {
  1791. default: return 0;
  1792. case COMPRESS_ZLIB:
  1793. {
  1794. const UInt windowBits=15, memLevel=8;
  1795. return (1<<(windowBits+2)) + (1<<(memLevel+9)); // taken from "ThirdPartyLibs\Zlib\zconf.h" and http://www.zlib.net/zlib_tech.html
  1796. }
  1797. #if SUPPORT_LZMA
  1798. case COMPRESS_LZMA:
  1799. {
  1800. const UInt dict_size=LZMADictSize(compression_level, uncompressed_size),
  1801. state_size=16*1024; // 16 KB default
  1802. return RoundU(dict_size*11.5f) + 6*1024*1024 + state_size; // (dict_size * 11.5 + 6 MB) + state_size, taken from "ThirdPartyLibs\LZMA\lzma\lzma.txt"
  1803. }
  1804. #endif
  1805. case COMPRESS_SNAPPY: return 32768+76490; // this was observed by monitoring calls to 'Alloc', which resulted in 1x32768 and 1x76490
  1806. //case COMPRESS_RLE : return 0; RLE doesn't use any extra memory
  1807. #if SUPPORT_LZ4
  1808. //case COMPRESS_LZ4: return 1<<LZ4_MEMORY_USAGE; 'LZ4_streamHC_t' and 'LZ4_stream_t' are allocated on the stack
  1809. #endif
  1810. #if SUPPORT_LIZARD
  1811. case COMPRESS_LIZARD: return Lizard_sizeofState(Mid(compression_level, LIZARD_MIN_CLEVEL, LIZARD_MAX_CLEVEL));
  1812. #endif
  1813. #if SUPPORT_ZSTD
  1814. case COMPRESS_ZSTD:
  1815. {
  1816. if(uncompressed_size<0)uncompressed_size=LONG_MAX; // if size is unknown then use max possible
  1817. U64 srcSize=(uncompressed_size ? uncompressed_size : 1); // don't use zero, because ZSTD treats it as unknown
  1818. ZSTD_compressionParameters params=ZSTD_getCParams(Mid(compression_level, 1, ZSTD_maxCLevel()), srcSize, 0);
  1819. #if !ZSTD_WINDOWLOG_SAVE
  1820. params.windowLog=ZSTDDictSizeLog2(uncompressed_size);
  1821. #endif
  1822. C Int window_size=1<<params.windowLog;
  1823. return MemtMemUsage( Min(window_size+ZSTD_BLOCKSIZE_MAX, uncompressed_size) ) // Memt s;
  1824. +MemtMemUsage(ZSTDSize(Min( ZSTD_BLOCKSIZE_MAX, uncompressed_size))) // Memt d;
  1825. +ZSTD_estimateCCtxSize_usingCParams(params); // ZSTD_CCtx
  1826. }
  1827. #endif
  1828. #if SUPPORT_BROTLI
  1829. case COMPRESS_BROTLI: return ;
  1830. #endif
  1831. #if SUPPORT_LZHAM
  1832. case COMPRESS_LZHAM:
  1833. {
  1834. /*Based on https://github.com/richgel999/lzham_codec
  1835. It's approximately (max_probes=128 at level -m4): comp_mem = min(512 * 1024, dict_size / 8) * max_probes * 6 + dict_size * 9 + 22020096
  1836. Compression mem usage examples from Windows lzhamtest_x64 (note the equation is pretty off for small dictionary sizes):
  1837. 32KB: 11MB
  1838. 128KB: 21MB
  1839. 512KB: 63MB
  1840. 1MB: 118MB
  1841. 8MB: 478MB
  1842. 64MB: 982MB
  1843. 128MB: 1558MB
  1844. 256MB: 2710MB
  1845. 512MB: 5014MB */
  1846. const UInt dict_size=LZHAMDictSize(uncompressed_size), max_probes=128;
  1847. return Min(512*1024, dict_size/8)*max_probes*6 + dict_size*9 + 22020096;
  1848. }
  1849. #endif
  1850. }
  1851. }
  1852. UInt DecompressionMemUsage(COMPRESS_TYPE type, Int compression_level, Long uncompressed_size)
  1853. {
  1854. switch(type)
  1855. {
  1856. default: return 0;
  1857. case COMPRESS_ZLIB:
  1858. {
  1859. const UInt windowBits=15;
  1860. return (1<<windowBits) + 1440*2*SIZE(Int); // taken from "ThirdPartyLibs\Zlib\zconf.h" and http://www.zlib.net/zlib_tech.html
  1861. }
  1862. #if SUPPORT_LZMA
  1863. case COMPRESS_LZMA:
  1864. {
  1865. const UInt dict_size=LZMADictSize(compression_level, uncompressed_size),
  1866. state_size=16*1024; // 16 KB default
  1867. return dict_size + state_size; // taken from "ThirdPartyLibs\LZMA\lzma\lzma.txt"
  1868. }
  1869. #endif
  1870. //case COMPRESS_SNAPPY: return 0; this was observed by monitoring calls to 'Alloc', which resulted in no calls
  1871. //case COMPRESS_RLE : return 0; RLE doesn't use any extra memory
  1872. #if SUPPORT_LZ4
  1873. //case COMPRESS_LZ4: return 1<<LZ4_MEMORY_USAGE; 'LZ4_streamDecode_t' is allocated on the stack
  1874. #endif
  1875. #if SUPPORT_LIZARD
  1876. //case COMPRESS_LIZARD: return 0; 'Lizard_streamDecode_t' is allocated on the stack
  1877. #endif
  1878. #if SUPPORT_ZSTD
  1879. case COMPRESS_ZSTD:
  1880. {
  1881. if(uncompressed_size<0)uncompressed_size=LONG_MAX; // if size is unknown then use max possible
  1882. #if ZSTD_WINDOWLOG_SAVE
  1883. U64 srcSize=(uncompressed_size ? uncompressed_size : 1); // don't use zero, because ZSTD treats it as unknown
  1884. ZSTD_compressionParameters params=ZSTD_getCParams(Mid(compression_level, 1, ZSTD_maxCLevel()), srcSize, 0);
  1885. C Int window_size=1<<params.windowLog;
  1886. #else
  1887. C Int window_size=1<<ZSTDDictSizeLog2(uncompressed_size);
  1888. #endif
  1889. return MemtMemUsage(ZSTDSize(Min(ZSTD_BLOCKSIZE_MAX, uncompressed_size))) // Memt s;
  1890. +MemtMemUsage( window_size+ZSTD_BLOCKSIZE_MAX ) // Memt d;
  1891. +ZSTD_estimateDCtxSize(); // ZSTD_DCtx
  1892. }
  1893. #endif
  1894. #if SUPPORT_BROTLI
  1895. case COMPRESS_BROTLI: return ;
  1896. #endif
  1897. #if SUPPORT_LZHAM
  1898. case COMPRESS_LZHAM:
  1899. {
  1900. /*Based on https://github.com/richgel999/lzham_codec
  1901. Buffered mode: decomp_mem = dict_size + ~34KB for work tables
  1902. Unbuffered mode: decomp_mem = ~34KB */
  1903. const UInt dict_size=LZHAMDictSize(uncompressed_size);
  1904. return dict_size + 34*1024;
  1905. }
  1906. #endif
  1907. }
  1908. }
  1909. /******************************************************************************/
  1910. }
  1911. /******************************************************************************/