fileio.c 131 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364
  1. /*
  2. * Copyright (c) Meta Platforms, Inc. and affiliates.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under both the BSD-style license (found in the
  6. * LICENSE file in the root directory of this source tree) and the GPLv2 (found
  7. * in the COPYING file in the root directory of this source tree).
  8. * You may select, at your option, one of the above-listed licenses.
  9. */
  10. /* *************************************
  11. * Compiler Options
  12. ***************************************/
  13. #ifdef _MSC_VER /* Visual */
  14. # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
  15. # pragma warning(disable : 4204) /* non-constant aggregate initializer */
  16. #endif
  17. #if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
  18. # define _POSIX_SOURCE 1 /* disable %llu warnings with MinGW on Windows */
  19. #endif
  20. /*-*************************************
  21. * Includes
  22. ***************************************/
  23. #include "platform.h" /* Large Files support, SET_BINARY_MODE */
  24. #include "util.h" /* UTIL_getFileSize, UTIL_isRegularFile, UTIL_isSameFile */
  25. #include <stdio.h> /* fprintf, open, fdopen, fread, _fileno, stdin, stdout */
  26. #include <stdlib.h> /* malloc, free */
  27. #include <string.h> /* strcmp, strlen */
  28. #include <time.h> /* clock_t, to measure process time */
  29. #include <fcntl.h> /* O_WRONLY */
  30. #include <assert.h>
  31. #include <errno.h> /* errno */
  32. #include <limits.h> /* INT_MAX */
  33. #include <signal.h>
  34. #include "timefn.h" /* UTIL_getTime, UTIL_clockSpanMicro */
  35. #if defined (_MSC_VER)
  36. # include <sys/stat.h>
  37. # include <io.h>
  38. #endif
  39. #include "fileio.h"
  40. #include "fileio_asyncio.h"
  41. #include "fileio_common.h"
  42. FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
  43. UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
  44. #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
  45. #include "../lib/zstd.h"
  46. #include "../lib/zstd_errors.h" /* ZSTD_error_frameParameter_windowTooLarge */
  47. #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
  48. # error #include <zlib.h>
  49. # if !defined(z_const)
  50. # define z_const
  51. # endif
  52. #endif
  53. #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
  54. # error #include <lzma.h>
  55. #endif
  56. #define LZ4_MAGICNUMBER 0x184D2204
  57. #if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
  58. # define LZ4F_ENABLE_OBSOLETE_ENUMS
  59. # error #include <lz4frame.h>
  60. # error #include <lz4.h>
  61. #endif
  62. char const* FIO_zlibVersion(void)
  63. {
  64. #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
  65. return zlibVersion();
  66. #else
  67. return "Unsupported";
  68. #endif
  69. }
  70. char const* FIO_lz4Version(void)
  71. {
  72. #if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
  73. /* LZ4_versionString() added in v1.7.3 */
  74. # if LZ4_VERSION_NUMBER >= 10703
  75. return LZ4_versionString();
  76. # else
  77. # define ZSTD_LZ4_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
  78. # define ZSTD_LZ4_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LZ4_VERSION)
  79. return ZSTD_LZ4_VERSION_STRING;
  80. # endif
  81. #else
  82. return "Unsupported";
  83. #endif
  84. }
  85. char const* FIO_lzmaVersion(void)
  86. {
  87. #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
  88. return lzma_version_string();
  89. #else
  90. return "Unsupported";
  91. #endif
  92. }
  93. /*-*************************************
  94. * Constants
  95. ***************************************/
  96. #define ADAPT_WINDOWLOG_DEFAULT 23 /* 8 MB */
  97. #define DICTSIZE_MAX (32 MB) /* protection against large input (attack scenario) */
  98. #define FNSPACE 30
  99. /* Default file permissions 0666 (modulated by umask) */
  100. /* Temporary restricted file permissions are used when we're going to
  101. * chmod/chown at the end of the operation. */
  102. #if !defined(_WIN32)
  103. /* These macros aren't defined on windows. */
  104. #define DEFAULT_FILE_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
  105. #define TEMPORARY_FILE_PERMISSIONS (S_IRUSR|S_IWUSR)
  106. #else
  107. #define DEFAULT_FILE_PERMISSIONS (0666)
  108. #define TEMPORARY_FILE_PERMISSIONS (0600)
  109. #endif
  110. /*-************************************
  111. * Signal (Ctrl-C trapping)
  112. **************************************/
  113. static const char* g_artefact = NULL;
  114. static void INThandler(int sig)
  115. {
  116. assert(sig==SIGINT); (void)sig;
  117. #if !defined(_MSC_VER)
  118. signal(sig, SIG_IGN); /* this invocation generates a buggy warning in Visual Studio */
  119. #endif
  120. if (g_artefact) {
  121. assert(UTIL_isRegularFile(g_artefact));
  122. remove(g_artefact);
  123. }
  124. DISPLAY("\n");
  125. exit(2);
  126. }
  127. static void addHandler(char const* dstFileName)
  128. {
  129. if (UTIL_isRegularFile(dstFileName)) {
  130. g_artefact = dstFileName;
  131. signal(SIGINT, INThandler);
  132. } else {
  133. g_artefact = NULL;
  134. }
  135. }
  136. /* Idempotent */
  137. static void clearHandler(void)
  138. {
  139. if (g_artefact) signal(SIGINT, SIG_DFL);
  140. g_artefact = NULL;
  141. }
  142. /*-*********************************************************
  143. * Termination signal trapping (Print debug stack trace)
  144. ***********************************************************/
  145. #if defined(__has_feature) && !defined(BACKTRACE_ENABLE) /* Clang compiler */
  146. # if (__has_feature(address_sanitizer))
  147. # define BACKTRACE_ENABLE 0
  148. # endif /* __has_feature(address_sanitizer) */
  149. #elif defined(__SANITIZE_ADDRESS__) && !defined(BACKTRACE_ENABLE) /* GCC compiler */
  150. # define BACKTRACE_ENABLE 0
  151. #endif
  152. #if !defined(BACKTRACE_ENABLE)
  153. /* automatic detector : backtrace enabled by default on linux+glibc and osx */
  154. # if (defined(__linux__) && (defined(__GLIBC__) && !defined(__UCLIBC__))) \
  155. || (defined(__APPLE__) && defined(__MACH__))
  156. # define BACKTRACE_ENABLE 1
  157. # else
  158. # define BACKTRACE_ENABLE 0
  159. # endif
  160. #endif
  161. /* note : after this point, BACKTRACE_ENABLE is necessarily defined */
  162. #if BACKTRACE_ENABLE
  163. #include <execinfo.h> /* backtrace, backtrace_symbols */
  164. #define MAX_STACK_FRAMES 50
  165. static void ABRThandler(int sig) {
  166. const char* name;
  167. void* addrlist[MAX_STACK_FRAMES];
  168. char** symbollist;
  169. int addrlen, i;
  170. switch (sig) {
  171. case SIGABRT: name = "SIGABRT"; break;
  172. case SIGFPE: name = "SIGFPE"; break;
  173. case SIGILL: name = "SIGILL"; break;
  174. case SIGINT: name = "SIGINT"; break;
  175. case SIGSEGV: name = "SIGSEGV"; break;
  176. default: name = "UNKNOWN";
  177. }
  178. DISPLAY("Caught %s signal, printing stack:\n", name);
  179. /* Retrieve current stack addresses. */
  180. addrlen = backtrace(addrlist, MAX_STACK_FRAMES);
  181. if (addrlen == 0) {
  182. DISPLAY("\n");
  183. return;
  184. }
  185. /* Create readable strings to each frame. */
  186. symbollist = backtrace_symbols(addrlist, addrlen);
  187. /* Print the stack trace, excluding calls handling the signal. */
  188. for (i = ZSTD_START_SYMBOLLIST_FRAME; i < addrlen; i++) {
  189. DISPLAY("%s\n", symbollist[i]);
  190. }
  191. free(symbollist);
  192. /* Reset and raise the signal so default handler runs. */
  193. signal(sig, SIG_DFL);
  194. raise(sig);
  195. }
  196. #endif
  197. void FIO_addAbortHandler(void)
  198. {
  199. #if BACKTRACE_ENABLE
  200. signal(SIGABRT, ABRThandler);
  201. signal(SIGFPE, ABRThandler);
  202. signal(SIGILL, ABRThandler);
  203. signal(SIGSEGV, ABRThandler);
  204. signal(SIGBUS, ABRThandler);
  205. #endif
  206. }
  207. /*-*************************************
  208. * Parameters: FIO_ctx_t
  209. ***************************************/
  210. /* typedef'd to FIO_ctx_t within fileio.h */
  211. struct FIO_ctx_s {
  212. /* file i/o info */
  213. int nbFilesTotal;
  214. int hasStdinInput;
  215. int hasStdoutOutput;
  216. /* file i/o state */
  217. int currFileIdx;
  218. int nbFilesProcessed;
  219. size_t totalBytesInput;
  220. size_t totalBytesOutput;
  221. };
  222. static int FIO_shouldDisplayFileSummary(FIO_ctx_t const* fCtx)
  223. {
  224. return fCtx->nbFilesTotal <= 1 || g_display_prefs.displayLevel >= 3;
  225. }
  226. static int FIO_shouldDisplayMultipleFileSummary(FIO_ctx_t const* fCtx)
  227. {
  228. int const shouldDisplay = (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1);
  229. assert(shouldDisplay || FIO_shouldDisplayFileSummary(fCtx) || fCtx->nbFilesProcessed == 0);
  230. return shouldDisplay;
  231. }
  232. /*-*************************************
  233. * Parameters: Initialization
  234. ***************************************/
  235. #define FIO_OVERLAP_LOG_NOTSET 9999
  236. #define FIO_LDM_PARAM_NOTSET 9999
  237. FIO_prefs_t* FIO_createPreferences(void)
  238. {
  239. FIO_prefs_t* const ret = (FIO_prefs_t*)malloc(sizeof(FIO_prefs_t));
  240. if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
  241. ret->compressionType = FIO_zstdCompression;
  242. ret->overwrite = 0;
  243. ret->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
  244. ret->dictIDFlag = 1;
  245. ret->checksumFlag = 1;
  246. ret->removeSrcFile = 0;
  247. ret->memLimit = 0;
  248. ret->nbWorkers = 1;
  249. ret->blockSize = 0;
  250. ret->overlapLog = FIO_OVERLAP_LOG_NOTSET;
  251. ret->adaptiveMode = 0;
  252. ret->rsyncable = 0;
  253. ret->minAdaptLevel = -50; /* initializing this value requires a constant, so ZSTD_minCLevel() doesn't work */
  254. ret->maxAdaptLevel = 22; /* initializing this value requires a constant, so ZSTD_maxCLevel() doesn't work */
  255. ret->ldmFlag = 0;
  256. ret->ldmHashLog = 0;
  257. ret->ldmMinMatch = 0;
  258. ret->ldmBucketSizeLog = FIO_LDM_PARAM_NOTSET;
  259. ret->ldmHashRateLog = FIO_LDM_PARAM_NOTSET;
  260. ret->streamSrcSize = 0;
  261. ret->targetCBlockSize = 0;
  262. ret->srcSizeHint = 0;
  263. ret->testMode = 0;
  264. ret->literalCompressionMode = ZSTD_ps_auto;
  265. ret->excludeCompressedFiles = 0;
  266. ret->allowBlockDevices = 0;
  267. ret->asyncIO = AIO_supported();
  268. ret->passThrough = -1;
  269. return ret;
  270. }
  271. FIO_ctx_t* FIO_createContext(void)
  272. {
  273. FIO_ctx_t* const ret = (FIO_ctx_t*)malloc(sizeof(FIO_ctx_t));
  274. if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
  275. ret->currFileIdx = 0;
  276. ret->hasStdinInput = 0;
  277. ret->hasStdoutOutput = 0;
  278. ret->nbFilesTotal = 1;
  279. ret->nbFilesProcessed = 0;
  280. ret->totalBytesInput = 0;
  281. ret->totalBytesOutput = 0;
  282. return ret;
  283. }
  284. void FIO_freePreferences(FIO_prefs_t* const prefs)
  285. {
  286. free(prefs);
  287. }
  288. void FIO_freeContext(FIO_ctx_t* const fCtx)
  289. {
  290. free(fCtx);
  291. }
  292. /*-*************************************
  293. * Parameters: Display Options
  294. ***************************************/
  295. void FIO_setNotificationLevel(int level) { g_display_prefs.displayLevel=level; }
  296. void FIO_setProgressSetting(FIO_progressSetting_e setting) { g_display_prefs.progressSetting = setting; }
  297. /*-*************************************
  298. * Parameters: Setters
  299. ***************************************/
  300. /* FIO_prefs_t functions */
  301. void FIO_setCompressionType(FIO_prefs_t* const prefs, FIO_compressionType_t compressionType) { prefs->compressionType = compressionType; }
  302. void FIO_overwriteMode(FIO_prefs_t* const prefs) { prefs->overwrite = 1; }
  303. void FIO_setSparseWrite(FIO_prefs_t* const prefs, int sparse) { prefs->sparseFileSupport = sparse; }
  304. void FIO_setDictIDFlag(FIO_prefs_t* const prefs, int dictIDFlag) { prefs->dictIDFlag = dictIDFlag; }
  305. void FIO_setChecksumFlag(FIO_prefs_t* const prefs, int checksumFlag) { prefs->checksumFlag = checksumFlag; }
  306. void FIO_setRemoveSrcFile(FIO_prefs_t* const prefs, int flag) { prefs->removeSrcFile = (flag!=0); }
  307. void FIO_setMemLimit(FIO_prefs_t* const prefs, unsigned memLimit) { prefs->memLimit = memLimit; }
  308. void FIO_setNbWorkers(FIO_prefs_t* const prefs, int nbWorkers) {
  309. #ifndef ZSTD_MULTITHREAD
  310. if (nbWorkers > 0) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n");
  311. #endif
  312. prefs->nbWorkers = nbWorkers;
  313. }
  314. void FIO_setExcludeCompressedFile(FIO_prefs_t* const prefs, int excludeCompressedFiles) { prefs->excludeCompressedFiles = excludeCompressedFiles; }
  315. void FIO_setAllowBlockDevices(FIO_prefs_t* const prefs, int allowBlockDevices) { prefs->allowBlockDevices = allowBlockDevices; }
  316. void FIO_setBlockSize(FIO_prefs_t* const prefs, int blockSize) {
  317. if (blockSize && prefs->nbWorkers==0)
  318. DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n");
  319. prefs->blockSize = blockSize;
  320. }
  321. void FIO_setOverlapLog(FIO_prefs_t* const prefs, int overlapLog){
  322. if (overlapLog && prefs->nbWorkers==0)
  323. DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n");
  324. prefs->overlapLog = overlapLog;
  325. }
  326. void FIO_setAdaptiveMode(FIO_prefs_t* const prefs, int adapt) {
  327. if ((adapt>0) && (prefs->nbWorkers==0))
  328. EXM_THROW(1, "Adaptive mode is not compatible with single thread mode \n");
  329. prefs->adaptiveMode = adapt;
  330. }
  331. void FIO_setUseRowMatchFinder(FIO_prefs_t* const prefs, int useRowMatchFinder) {
  332. prefs->useRowMatchFinder = useRowMatchFinder;
  333. }
  334. void FIO_setRsyncable(FIO_prefs_t* const prefs, int rsyncable) {
  335. if ((rsyncable>0) && (prefs->nbWorkers==0))
  336. EXM_THROW(1, "Rsyncable mode is not compatible with single thread mode \n");
  337. prefs->rsyncable = rsyncable;
  338. }
  339. void FIO_setStreamSrcSize(FIO_prefs_t* const prefs, size_t streamSrcSize) {
  340. prefs->streamSrcSize = streamSrcSize;
  341. }
  342. void FIO_setTargetCBlockSize(FIO_prefs_t* const prefs, size_t targetCBlockSize) {
  343. prefs->targetCBlockSize = targetCBlockSize;
  344. }
  345. void FIO_setSrcSizeHint(FIO_prefs_t* const prefs, size_t srcSizeHint) {
  346. prefs->srcSizeHint = (int)MIN((size_t)INT_MAX, srcSizeHint);
  347. }
  348. void FIO_setTestMode(FIO_prefs_t* const prefs, int testMode) {
  349. prefs->testMode = (testMode!=0);
  350. }
  351. void FIO_setLiteralCompressionMode(
  352. FIO_prefs_t* const prefs,
  353. ZSTD_paramSwitch_e mode) {
  354. prefs->literalCompressionMode = mode;
  355. }
  356. void FIO_setAdaptMin(FIO_prefs_t* const prefs, int minCLevel)
  357. {
  358. #ifndef ZSTD_NOCOMPRESS
  359. assert(minCLevel >= ZSTD_minCLevel());
  360. #endif
  361. prefs->minAdaptLevel = minCLevel;
  362. }
  363. void FIO_setAdaptMax(FIO_prefs_t* const prefs, int maxCLevel)
  364. {
  365. prefs->maxAdaptLevel = maxCLevel;
  366. }
  367. void FIO_setLdmFlag(FIO_prefs_t* const prefs, unsigned ldmFlag) {
  368. prefs->ldmFlag = (ldmFlag>0);
  369. }
  370. void FIO_setLdmHashLog(FIO_prefs_t* const prefs, int ldmHashLog) {
  371. prefs->ldmHashLog = ldmHashLog;
  372. }
  373. void FIO_setLdmMinMatch(FIO_prefs_t* const prefs, int ldmMinMatch) {
  374. prefs->ldmMinMatch = ldmMinMatch;
  375. }
  376. void FIO_setLdmBucketSizeLog(FIO_prefs_t* const prefs, int ldmBucketSizeLog) {
  377. prefs->ldmBucketSizeLog = ldmBucketSizeLog;
  378. }
  379. void FIO_setLdmHashRateLog(FIO_prefs_t* const prefs, int ldmHashRateLog) {
  380. prefs->ldmHashRateLog = ldmHashRateLog;
  381. }
  382. void FIO_setPatchFromMode(FIO_prefs_t* const prefs, int value)
  383. {
  384. prefs->patchFromMode = value != 0;
  385. }
  386. void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
  387. {
  388. prefs->contentSize = value != 0;
  389. }
  390. void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, int value) {
  391. #ifdef ZSTD_MULTITHREAD
  392. prefs->asyncIO = value;
  393. #else
  394. (void) prefs;
  395. (void) value;
  396. DISPLAYLEVEL(2, "Note : asyncio is disabled (lack of multithreading support) \n");
  397. #endif
  398. }
  399. void FIO_setPassThroughFlag(FIO_prefs_t* const prefs, int value) {
  400. prefs->passThrough = (value != 0);
  401. }
  402. void FIO_setMMapDict(FIO_prefs_t* const prefs, ZSTD_paramSwitch_e value)
  403. {
  404. prefs->mmapDict = value;
  405. }
  406. /* FIO_ctx_t functions */
  407. void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) {
  408. fCtx->hasStdoutOutput = value;
  409. }
  410. void FIO_setNbFilesTotal(FIO_ctx_t* const fCtx, int value)
  411. {
  412. fCtx->nbFilesTotal = value;
  413. }
  414. void FIO_determineHasStdinInput(FIO_ctx_t* const fCtx, const FileNamesTable* const filenames) {
  415. size_t i = 0;
  416. for ( ; i < filenames->tableSize; ++i) {
  417. if (!strcmp(stdinmark, filenames->fileNames[i])) {
  418. fCtx->hasStdinInput = 1;
  419. return;
  420. }
  421. }
  422. }
  423. /*-*************************************
  424. * Functions
  425. ***************************************/
  426. /** FIO_removeFile() :
  427. * @result : Unlink `fileName`, even if it's read-only */
  428. static int FIO_removeFile(const char* path)
  429. {
  430. stat_t statbuf;
  431. if (!UTIL_stat(path, &statbuf)) {
  432. DISPLAYLEVEL(2, "zstd: Failed to stat %s while trying to remove it\n", path);
  433. return 0;
  434. }
  435. if (!UTIL_isRegularFileStat(&statbuf)) {
  436. DISPLAYLEVEL(2, "zstd: Refusing to remove non-regular file %s\n", path);
  437. return 0;
  438. }
  439. #if defined(_WIN32) || defined(WIN32)
  440. /* windows doesn't allow remove read-only files,
  441. * so try to make it writable first */
  442. if (!(statbuf.st_mode & _S_IWRITE)) {
  443. UTIL_chmod(path, &statbuf, _S_IWRITE);
  444. }
  445. #endif
  446. return remove(path);
  447. }
  448. /** FIO_openSrcFile() :
  449. * condition : `srcFileName` must be non-NULL. `prefs` may be NULL.
  450. * @result : FILE* to `srcFileName`, or NULL if it fails */
  451. static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFileName, stat_t* statbuf)
  452. {
  453. int allowBlockDevices = prefs != NULL ? prefs->allowBlockDevices : 0;
  454. assert(srcFileName != NULL);
  455. assert(statbuf != NULL);
  456. if (!strcmp (srcFileName, stdinmark)) {
  457. DISPLAYLEVEL(4,"Using stdin for input \n");
  458. SET_BINARY_MODE(stdin);
  459. return stdin;
  460. }
  461. if (!UTIL_stat(srcFileName, statbuf)) {
  462. DISPLAYLEVEL(1, "zstd: can't stat %s : %s -- ignored \n",
  463. srcFileName, strerror(errno));
  464. return NULL;
  465. }
  466. if (!UTIL_isRegularFileStat(statbuf)
  467. && !UTIL_isFIFOStat(statbuf)
  468. && !(allowBlockDevices && UTIL_isBlockDevStat(statbuf))
  469. ) {
  470. DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n",
  471. srcFileName);
  472. return NULL;
  473. }
  474. { FILE* const f = fopen(srcFileName, "rb");
  475. if (f == NULL)
  476. DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
  477. return f;
  478. }
  479. }
  480. /** FIO_openDstFile() :
  481. * condition : `dstFileName` must be non-NULL.
  482. * @result : FILE* to `dstFileName`, or NULL if it fails */
  483. static FILE*
  484. FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
  485. const char* srcFileName, const char* dstFileName,
  486. const int mode)
  487. {
  488. int isDstRegFile;
  489. if (prefs->testMode) return NULL; /* do not open file in test mode */
  490. assert(dstFileName != NULL);
  491. if (!strcmp (dstFileName, stdoutmark)) {
  492. DISPLAYLEVEL(4,"Using stdout for output \n");
  493. SET_BINARY_MODE(stdout);
  494. if (prefs->sparseFileSupport == 1) {
  495. prefs->sparseFileSupport = 0;
  496. DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
  497. }
  498. return stdout;
  499. }
  500. /* ensure dst is not the same as src */
  501. if (srcFileName != NULL && UTIL_isSameFile(srcFileName, dstFileName)) {
  502. DISPLAYLEVEL(1, "zstd: Refusing to open an output file which will overwrite the input file \n");
  503. return NULL;
  504. }
  505. isDstRegFile = UTIL_isRegularFile(dstFileName); /* invoke once */
  506. if (prefs->sparseFileSupport == 1) {
  507. prefs->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
  508. if (!isDstRegFile) {
  509. prefs->sparseFileSupport = 0;
  510. DISPLAYLEVEL(4, "Sparse File Support is disabled when output is not a file \n");
  511. }
  512. }
  513. if (isDstRegFile) {
  514. /* Check if destination file already exists */
  515. #if !defined(_WIN32)
  516. /* this test does not work on Windows :
  517. * `NUL` and `nul` are detected as regular files */
  518. if (!strcmp(dstFileName, nulmark)) {
  519. EXM_THROW(40, "%s is unexpectedly categorized as a regular file",
  520. dstFileName);
  521. }
  522. #endif
  523. if (!prefs->overwrite) {
  524. if (g_display_prefs.displayLevel <= 1) {
  525. /* No interaction possible */
  526. DISPLAYLEVEL(1, "zstd: %s already exists; not overwritten \n",
  527. dstFileName);
  528. return NULL;
  529. }
  530. DISPLAY("zstd: %s already exists; ", dstFileName);
  531. if (UTIL_requireUserConfirmation("overwrite (y/n) ? ", "Not overwritten \n", "yY", fCtx->hasStdinInput))
  532. return NULL;
  533. }
  534. /* need to unlink */
  535. FIO_removeFile(dstFileName);
  536. }
  537. {
  538. #if defined(_WIN32)
  539. /* Windows requires opening the file as a "binary" file to avoid
  540. * mangling. This macro doesn't exist on unix. */
  541. const int openflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
  542. const int fd = _open(dstFileName, openflags, mode);
  543. FILE* f = NULL;
  544. if (fd != -1) {
  545. f = _fdopen(fd, "wb");
  546. }
  547. #else
  548. const int openflags = O_WRONLY|O_CREAT|O_TRUNC;
  549. const int fd = open(dstFileName, openflags, mode);
  550. FILE* f = NULL;
  551. if (fd != -1) {
  552. f = fdopen(fd, "wb");
  553. }
  554. #endif
  555. if (f == NULL) {
  556. DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
  557. } else {
  558. /* An increased buffer size can provide a significant performance
  559. * boost on some platforms. Note that providing a NULL buf with a
  560. * size that's not 0 is not defined in ANSI C, but is defined in an
  561. * extension. There are three possibilities here:
  562. * 1. Libc supports the extended version and everything is good.
  563. * 2. Libc ignores the size when buf is NULL, in which case
  564. * everything will continue as if we didn't call `setvbuf()`.
  565. * 3. We fail the call and execution continues but a warning
  566. * message might be shown.
  567. * In all cases due execution continues. For now, I believe that
  568. * this is a more cost-effective solution than managing the buffers
  569. * allocations ourselves (will require an API change).
  570. */
  571. if (setvbuf(f, NULL, _IOFBF, 1 MB)) {
  572. DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName);
  573. }
  574. }
  575. return f;
  576. }
  577. }
  578. /* FIO_getDictFileStat() :
  579. */
  580. static void FIO_getDictFileStat(const char* fileName, stat_t* dictFileStat) {
  581. assert(dictFileStat != NULL);
  582. if (fileName == NULL) return;
  583. if (!UTIL_stat(fileName, dictFileStat)) {
  584. EXM_THROW(31, "Stat failed on dictionary file %s: %s", fileName, strerror(errno));
  585. }
  586. if (!UTIL_isRegularFileStat(dictFileStat)) {
  587. EXM_THROW(32, "Dictionary %s must be a regular file.", fileName);
  588. }
  589. }
  590. /* FIO_setDictBufferMalloc() :
  591. * allocates a buffer, pointed by `dict->dictBuffer`,
  592. * loads `filename` content into it, up to DICTSIZE_MAX bytes.
  593. * @return : loaded size
  594. * if fileName==NULL, returns 0 and a NULL pointer
  595. */
  596. static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
  597. {
  598. FILE* fileHandle;
  599. U64 fileSize;
  600. void** bufferPtr = &dict->dictBuffer;
  601. assert(bufferPtr != NULL);
  602. assert(dictFileStat != NULL);
  603. *bufferPtr = NULL;
  604. if (fileName == NULL) return 0;
  605. DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
  606. fileHandle = fopen(fileName, "rb");
  607. if (fileHandle == NULL) {
  608. EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
  609. }
  610. fileSize = UTIL_getFileSizeStat(dictFileStat);
  611. {
  612. size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
  613. if (fileSize > dictSizeMax) {
  614. EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
  615. fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
  616. }
  617. }
  618. *bufferPtr = malloc((size_t)fileSize);
  619. if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
  620. { size_t const readSize = fread(*bufferPtr, 1, (size_t)fileSize, fileHandle);
  621. if (readSize != fileSize) {
  622. EXM_THROW(35, "Error reading dictionary file %s : %s",
  623. fileName, strerror(errno));
  624. }
  625. }
  626. fclose(fileHandle);
  627. return (size_t)fileSize;
  628. }
  629. #if (PLATFORM_POSIX_VERSION > 0)
  630. #include <sys/mman.h>
  631. static void FIO_munmap(FIO_Dict_t* dict)
  632. {
  633. munmap(dict->dictBuffer, dict->dictBufferSize);
  634. dict->dictBuffer = NULL;
  635. dict->dictBufferSize = 0;
  636. }
  637. static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
  638. {
  639. int fileHandle;
  640. U64 fileSize;
  641. void** bufferPtr = &dict->dictBuffer;
  642. assert(bufferPtr != NULL);
  643. assert(dictFileStat != NULL);
  644. *bufferPtr = NULL;
  645. if (fileName == NULL) return 0;
  646. DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
  647. fileHandle = open(fileName, O_RDONLY);
  648. if (fileHandle == -1) {
  649. EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
  650. }
  651. fileSize = UTIL_getFileSizeStat(dictFileStat);
  652. {
  653. size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
  654. if (fileSize > dictSizeMax) {
  655. EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
  656. fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
  657. }
  658. }
  659. *bufferPtr = mmap(NULL, (size_t)fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0);
  660. if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
  661. close(fileHandle);
  662. return (size_t)fileSize;
  663. }
  664. #elif defined(_MSC_VER) || defined(_WIN32)
  665. #include <windows.h>
  666. static void FIO_munmap(FIO_Dict_t* dict)
  667. {
  668. UnmapViewOfFile(dict->dictBuffer);
  669. CloseHandle(dict->dictHandle);
  670. dict->dictBuffer = NULL;
  671. dict->dictBufferSize = 0;
  672. }
  673. static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
  674. {
  675. HANDLE fileHandle, mapping;
  676. U64 fileSize;
  677. void** bufferPtr = &dict->dictBuffer;
  678. assert(bufferPtr != NULL);
  679. assert(dictFileStat != NULL);
  680. *bufferPtr = NULL;
  681. if (fileName == NULL) return 0;
  682. DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
  683. fileHandle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
  684. if (fileHandle == INVALID_HANDLE_VALUE) {
  685. EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
  686. }
  687. fileSize = UTIL_getFileSizeStat(dictFileStat);
  688. {
  689. size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
  690. if (fileSize > dictSizeMax) {
  691. EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
  692. fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
  693. }
  694. }
  695. mapping = CreateFileMapping(fileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
  696. if (mapping == NULL) {
  697. EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno));
  698. }
  699. *bufferPtr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, (DWORD)fileSize); /* we can only cast to DWORD here because dictSize <= 2GB */
  700. if (*bufferPtr==NULL) EXM_THROW(36, "%s", strerror(errno));
  701. dict->dictHandle = fileHandle;
  702. return (size_t)fileSize;
  703. }
  704. #else
  705. static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
  706. {
  707. return FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
  708. }
  709. static void FIO_munmap(FIO_Dict_t* dict) {
  710. free(dict->dictBuffer);
  711. dict->dictBuffer = NULL;
  712. dict->dictBufferSize = 0;
  713. }
  714. #endif
  715. static void FIO_freeDict(FIO_Dict_t* dict) {
  716. if (dict->dictBufferType == FIO_mallocDict) {
  717. free(dict->dictBuffer);
  718. dict->dictBuffer = NULL;
  719. dict->dictBufferSize = 0;
  720. } else if (dict->dictBufferType == FIO_mmapDict) {
  721. FIO_munmap(dict);
  722. } else {
  723. assert(0); /* Should not reach this case */
  724. }
  725. }
  726. static void FIO_initDict(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat, FIO_dictBufferType_t dictBufferType) {
  727. dict->dictBufferType = dictBufferType;
  728. if (dict->dictBufferType == FIO_mallocDict) {
  729. dict->dictBufferSize = FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
  730. } else if (dict->dictBufferType == FIO_mmapDict) {
  731. dict->dictBufferSize = FIO_setDictBufferMMap(dict, fileName, prefs, dictFileStat);
  732. } else {
  733. assert(0); /* Should not reach this case */
  734. }
  735. }
  736. /* FIO_checkFilenameCollisions() :
  737. * Checks for and warns if there are any files that would have the same output path
  738. */
  739. int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
  740. const char **filenameTableSorted, *prevElem, *filename;
  741. unsigned u;
  742. filenameTableSorted = (const char**) malloc(sizeof(char*) * nbFiles);
  743. if (!filenameTableSorted) {
  744. DISPLAYLEVEL(1, "Allocation error during filename collision checking \n");
  745. return 1;
  746. }
  747. for (u = 0; u < nbFiles; ++u) {
  748. filename = strrchr(filenameTable[u], PATH_SEP);
  749. if (filename == NULL) {
  750. filenameTableSorted[u] = filenameTable[u];
  751. } else {
  752. filenameTableSorted[u] = filename+1;
  753. }
  754. }
  755. qsort((void*)filenameTableSorted, nbFiles, sizeof(char*), UTIL_compareStr);
  756. prevElem = filenameTableSorted[0];
  757. for (u = 1; u < nbFiles; ++u) {
  758. if (strcmp(prevElem, filenameTableSorted[u]) == 0) {
  759. DISPLAYLEVEL(2, "WARNING: Two files have same filename: %s\n", prevElem);
  760. }
  761. prevElem = filenameTableSorted[u];
  762. }
  763. free((void*)filenameTableSorted);
  764. return 0;
  765. }
  766. static const char*
  767. extractFilename(const char* path, char separator)
  768. {
  769. const char* search = strrchr(path, separator);
  770. if (search == NULL) return path;
  771. return search+1;
  772. }
  773. /* FIO_createFilename_fromOutDir() :
  774. * Takes a source file name and specified output directory, and
  775. * allocates memory for and returns a pointer to final path.
  776. * This function never returns an error (it may abort() in case of pb)
  777. */
  778. static char*
  779. FIO_createFilename_fromOutDir(const char* path, const char* outDirName, const size_t suffixLen)
  780. {
  781. const char* filenameStart;
  782. char separator;
  783. char* result;
  784. #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
  785. separator = '\\';
  786. #else
  787. separator = '/';
  788. #endif
  789. filenameStart = extractFilename(path, separator);
  790. #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
  791. filenameStart = extractFilename(filenameStart, '/'); /* sometimes, '/' separator is also used on Windows (mingw+msys2) */
  792. #endif
  793. result = (char*) calloc(1, strlen(outDirName) + 1 + strlen(filenameStart) + suffixLen + 1);
  794. if (!result) {
  795. EXM_THROW(30, "zstd: FIO_createFilename_fromOutDir: %s", strerror(errno));
  796. }
  797. memcpy(result, outDirName, strlen(outDirName));
  798. if (outDirName[strlen(outDirName)-1] == separator) {
  799. memcpy(result + strlen(outDirName), filenameStart, strlen(filenameStart));
  800. } else {
  801. memcpy(result + strlen(outDirName), &separator, 1);
  802. memcpy(result + strlen(outDirName) + 1, filenameStart, strlen(filenameStart));
  803. }
  804. return result;
  805. }
  806. /* FIO_highbit64() :
  807. * gives position of highest bit.
  808. * note : only works for v > 0 !
  809. */
  810. static unsigned FIO_highbit64(unsigned long long v)
  811. {
  812. unsigned count = 0;
  813. assert(v != 0);
  814. v >>= 1;
  815. while (v) { v >>= 1; count++; }
  816. return count;
  817. }
  818. static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
  819. unsigned long long const dictSize,
  820. unsigned long long const maxSrcFileSize)
  821. {
  822. unsigned long long maxSize = MAX(prefs->memLimit, MAX(dictSize, maxSrcFileSize));
  823. unsigned const maxWindowSize = (1U << ZSTD_WINDOWLOG_MAX);
  824. if (maxSize == UTIL_FILESIZE_UNKNOWN)
  825. EXM_THROW(42, "Using --patch-from with stdin requires --stream-size");
  826. assert(maxSize != UTIL_FILESIZE_UNKNOWN);
  827. if (maxSize > maxWindowSize)
  828. EXM_THROW(42, "Can't handle files larger than %u GB\n", maxWindowSize/(1 GB));
  829. FIO_setMemLimit(prefs, (unsigned)maxSize);
  830. }
  831. /* FIO_multiFilesConcatWarning() :
  832. * This function handles logic when processing multiple files with -o or -c, displaying the appropriate warnings/prompts.
  833. * Returns 1 if the console should abort, 0 if console should proceed.
  834. *
  835. * If output is stdout or test mode is active, check that `--rm` disabled.
  836. *
  837. * If there is just 1 file to process, zstd will proceed as usual.
  838. * If each file get processed into its own separate destination file, proceed as usual.
  839. *
  840. * When multiple files are processed into a single output,
  841. * display a warning message, then disable --rm if it's set.
  842. *
  843. * If -f is specified or if output is stdout, just proceed.
  844. * If output is set with -o, prompt for confirmation.
  845. */
  846. static int FIO_multiFilesConcatWarning(const FIO_ctx_t* fCtx, FIO_prefs_t* prefs, const char* outFileName, int displayLevelCutoff)
  847. {
  848. if (fCtx->hasStdoutOutput) {
  849. if (prefs->removeSrcFile)
  850. /* this should not happen ; hard fail, to protect user's data
  851. * note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
  852. EXM_THROW(43, "It's not allowed to remove input files when processed output is piped to stdout. "
  853. "This scenario is not supposed to be possible. "
  854. "This is a programming error. File an issue for it to be fixed.");
  855. }
  856. if (prefs->testMode) {
  857. if (prefs->removeSrcFile)
  858. /* this should not happen ; hard fail, to protect user's data
  859. * note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
  860. EXM_THROW(43, "Test mode shall not remove input files! "
  861. "This scenario is not supposed to be possible. "
  862. "This is a programming error. File an issue for it to be fixed.");
  863. return 0;
  864. }
  865. if (fCtx->nbFilesTotal == 1) return 0;
  866. assert(fCtx->nbFilesTotal > 1);
  867. if (!outFileName) return 0;
  868. if (fCtx->hasStdoutOutput) {
  869. DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
  870. } else {
  871. DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
  872. }
  873. DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate original file names nor directory structure. \n")
  874. /* multi-input into single output : --rm is not allowed */
  875. if (prefs->removeSrcFile) {
  876. DISPLAYLEVEL(2, "Since it's a destructive operation, input files will not be removed. \n");
  877. prefs->removeSrcFile = 0;
  878. }
  879. if (fCtx->hasStdoutOutput) return 0;
  880. if (prefs->overwrite) return 0;
  881. /* multiple files concatenated into single destination file using -o without -f */
  882. if (g_display_prefs.displayLevel <= displayLevelCutoff) {
  883. /* quiet mode => no prompt => fail automatically */
  884. DISPLAYLEVEL(1, "Concatenating multiple processed inputs into a single output loses file metadata. \n");
  885. DISPLAYLEVEL(1, "Aborting. \n");
  886. return 1;
  887. }
  888. /* normal mode => prompt */
  889. return UTIL_requireUserConfirmation("Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
  890. }
  891. static ZSTD_inBuffer setInBuffer(const void* buf, size_t s, size_t pos)
  892. {
  893. ZSTD_inBuffer i;
  894. i.src = buf;
  895. i.size = s;
  896. i.pos = pos;
  897. return i;
  898. }
  899. static ZSTD_outBuffer setOutBuffer(void* buf, size_t s, size_t pos)
  900. {
  901. ZSTD_outBuffer o;
  902. o.dst = buf;
  903. o.size = s;
  904. o.pos = pos;
  905. return o;
  906. }
  907. #ifndef ZSTD_NOCOMPRESS
  908. /* **********************************************************************
  909. * Compression
  910. ************************************************************************/
  911. typedef struct {
  912. FIO_Dict_t dict;
  913. const char* dictFileName;
  914. stat_t dictFileStat;
  915. ZSTD_CStream* cctx;
  916. WritePoolCtx_t *writeCtx;
  917. ReadPoolCtx_t *readCtx;
  918. } cRess_t;
  919. /** ZSTD_cycleLog() :
  920. * condition for correct operation : hashLog > 1 */
  921. static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)
  922. {
  923. U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);
  924. assert(hashLog > 1);
  925. return hashLog - btScale;
  926. }
  927. static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
  928. ZSTD_compressionParameters* comprParams,
  929. unsigned long long const dictSize,
  930. unsigned long long const maxSrcFileSize,
  931. int cLevel)
  932. {
  933. unsigned const fileWindowLog = FIO_highbit64(maxSrcFileSize) + 1;
  934. ZSTD_compressionParameters const cParams = ZSTD_getCParams(cLevel, (size_t)maxSrcFileSize, (size_t)dictSize);
  935. FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, maxSrcFileSize);
  936. if (fileWindowLog > ZSTD_WINDOWLOG_MAX)
  937. DISPLAYLEVEL(1, "Max window log exceeded by file (compression ratio will suffer)\n");
  938. comprParams->windowLog = MAX(ZSTD_WINDOWLOG_MIN, MIN(ZSTD_WINDOWLOG_MAX, fileWindowLog));
  939. if (fileWindowLog > ZSTD_cycleLog(cParams.chainLog, cParams.strategy)) {
  940. if (!prefs->ldmFlag)
  941. DISPLAYLEVEL(1, "long mode automatically triggered\n");
  942. FIO_setLdmFlag(prefs, 1);
  943. }
  944. if (cParams.strategy >= ZSTD_btopt) {
  945. DISPLAYLEVEL(1, "[Optimal parser notes] Consider the following to improve patch size at the cost of speed:\n");
  946. DISPLAYLEVEL(1, "- Use --single-thread mode in the zstd cli\n");
  947. DISPLAYLEVEL(1, "- Set a larger targetLength (e.g. --zstd=targetLength=4096)\n");
  948. DISPLAYLEVEL(1, "- Set a larger chainLog (e.g. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
  949. DISPLAYLEVEL(1, "Also consider playing around with searchLog and hashLog\n");
  950. }
  951. }
  952. static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
  953. const char* dictFileName, unsigned long long const maxSrcFileSize,
  954. int cLevel, ZSTD_compressionParameters comprParams) {
  955. int useMMap = prefs->mmapDict == ZSTD_ps_enable;
  956. int forceNoUseMMap = prefs->mmapDict == ZSTD_ps_disable;
  957. FIO_dictBufferType_t dictBufferType;
  958. cRess_t ress;
  959. memset(&ress, 0, sizeof(ress));
  960. DISPLAYLEVEL(6, "FIO_createCResources \n");
  961. ress.cctx = ZSTD_createCCtx();
  962. if (ress.cctx == NULL)
  963. EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx",
  964. strerror(errno));
  965. FIO_getDictFileStat(dictFileName, &ress.dictFileStat);
  966. /* need to update memLimit before calling createDictBuffer
  967. * because of memLimit check inside it */
  968. if (prefs->patchFromMode) {
  969. U64 const dictSize = UTIL_getFileSizeStat(&ress.dictFileStat);
  970. unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize;
  971. useMMap |= dictSize > prefs->memLimit;
  972. FIO_adjustParamsForPatchFromMode(prefs, &comprParams, dictSize, ssSize > 0 ? ssSize : maxSrcFileSize, cLevel);
  973. }
  974. dictBufferType = (useMMap && !forceNoUseMMap) ? FIO_mmapDict : FIO_mallocDict;
  975. FIO_initDict(&ress.dict, dictFileName, prefs, &ress.dictFileStat, dictBufferType); /* works with dictFileName==NULL */
  976. ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_CStreamOutSize());
  977. ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_CStreamInSize());
  978. /* Advanced parameters, including dictionary */
  979. if (dictFileName && (ress.dict.dictBuffer==NULL))
  980. EXM_THROW(32, "allocation error : can't create dictBuffer");
  981. ress.dictFileName = dictFileName;
  982. if (prefs->adaptiveMode && !prefs->ldmFlag && !comprParams.windowLog)
  983. comprParams.windowLog = ADAPT_WINDOWLOG_DEFAULT;
  984. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_contentSizeFlag, prefs->contentSize) ); /* always enable content size when available (note: supposed to be default) */
  985. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_dictIDFlag, prefs->dictIDFlag) );
  986. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_checksumFlag, prefs->checksumFlag) );
  987. /* compression level */
  988. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, cLevel) );
  989. /* max compressed block size */
  990. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetCBlockSize, (int)prefs->targetCBlockSize) );
  991. /* source size hint */
  992. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_srcSizeHint, (int)prefs->srcSizeHint) );
  993. /* long distance matching */
  994. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableLongDistanceMatching, prefs->ldmFlag) );
  995. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashLog, prefs->ldmHashLog) );
  996. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmMinMatch, prefs->ldmMinMatch) );
  997. if (prefs->ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) {
  998. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmBucketSizeLog, prefs->ldmBucketSizeLog) );
  999. }
  1000. if (prefs->ldmHashRateLog != FIO_LDM_PARAM_NOTSET) {
  1001. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashRateLog, prefs->ldmHashRateLog) );
  1002. }
  1003. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_useRowMatchFinder, prefs->useRowMatchFinder));
  1004. /* compression parameters */
  1005. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_windowLog, (int)comprParams.windowLog) );
  1006. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_chainLog, (int)comprParams.chainLog) );
  1007. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_hashLog, (int)comprParams.hashLog) );
  1008. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_searchLog, (int)comprParams.searchLog) );
  1009. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_minMatch, (int)comprParams.minMatch) );
  1010. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetLength, (int)comprParams.targetLength) );
  1011. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_strategy, (int)comprParams.strategy) );
  1012. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_literalCompressionMode, (int)prefs->literalCompressionMode) );
  1013. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableDedicatedDictSearch, 1) );
  1014. /* multi-threading */
  1015. #ifdef ZSTD_MULTITHREAD
  1016. DISPLAYLEVEL(5,"set nb workers = %u \n", prefs->nbWorkers);
  1017. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_nbWorkers, prefs->nbWorkers) );
  1018. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_jobSize, prefs->blockSize) );
  1019. if (prefs->overlapLog != FIO_OVERLAP_LOG_NOTSET) {
  1020. DISPLAYLEVEL(3,"set overlapLog = %u \n", prefs->overlapLog);
  1021. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_overlapLog, prefs->overlapLog) );
  1022. }
  1023. CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_rsyncable, prefs->rsyncable) );
  1024. #endif
  1025. /* dictionary */
  1026. if (prefs->patchFromMode) {
  1027. CHECK( ZSTD_CCtx_refPrefix(ress.cctx, ress.dict.dictBuffer, ress.dict.dictBufferSize) );
  1028. } else {
  1029. CHECK( ZSTD_CCtx_loadDictionary_byReference(ress.cctx, ress.dict.dictBuffer, ress.dict.dictBufferSize) );
  1030. }
  1031. return ress;
  1032. }
  1033. static void FIO_freeCResources(cRess_t* const ress)
  1034. {
  1035. FIO_freeDict(&(ress->dict));
  1036. AIO_WritePool_free(ress->writeCtx);
  1037. AIO_ReadPool_free(ress->readCtx);
  1038. ZSTD_freeCStream(ress->cctx); /* never fails */
  1039. }
  1040. #ifdef ZSTD_GZCOMPRESS
  1041. static unsigned long long
  1042. FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but not changed */
  1043. const char* srcFileName, U64 const srcFileSize,
  1044. int compressionLevel, U64* readsize)
  1045. {
  1046. unsigned long long inFileSize = 0, outFileSize = 0;
  1047. z_stream strm;
  1048. IOJob_t *writeJob = NULL;
  1049. if (compressionLevel > Z_BEST_COMPRESSION)
  1050. compressionLevel = Z_BEST_COMPRESSION;
  1051. strm.zalloc = Z_NULL;
  1052. strm.zfree = Z_NULL;
  1053. strm.opaque = Z_NULL;
  1054. { int const ret = deflateInit2(&strm, compressionLevel, Z_DEFLATED,
  1055. 15 /* maxWindowLogSize */ + 16 /* gzip only */,
  1056. 8, Z_DEFAULT_STRATEGY); /* see https://www.zlib.net/manual.html */
  1057. if (ret != Z_OK) {
  1058. EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
  1059. } }
  1060. writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  1061. strm.next_in = 0;
  1062. strm.avail_in = 0;
  1063. strm.next_out = (Bytef*)writeJob->buffer;
  1064. strm.avail_out = (uInt)writeJob->bufferSize;
  1065. while (1) {
  1066. int ret;
  1067. if (strm.avail_in == 0) {
  1068. AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
  1069. if (ress->readCtx->srcBufferLoaded == 0) break;
  1070. inFileSize += ress->readCtx->srcBufferLoaded;
  1071. strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
  1072. strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
  1073. }
  1074. {
  1075. size_t const availBefore = strm.avail_in;
  1076. ret = deflate(&strm, Z_NO_FLUSH);
  1077. AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
  1078. }
  1079. if (ret != Z_OK)
  1080. EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
  1081. { size_t const cSize = writeJob->bufferSize - strm.avail_out;
  1082. if (cSize) {
  1083. writeJob->usedBufferSize = cSize;
  1084. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1085. outFileSize += cSize;
  1086. strm.next_out = (Bytef*)writeJob->buffer;
  1087. strm.avail_out = (uInt)writeJob->bufferSize;
  1088. } }
  1089. if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
  1090. DISPLAYUPDATE_PROGRESS(
  1091. "\rRead : %u MB ==> %.2f%% ",
  1092. (unsigned)(inFileSize>>20),
  1093. (double)outFileSize/(double)inFileSize*100)
  1094. } else {
  1095. DISPLAYUPDATE_PROGRESS(
  1096. "\rRead : %u / %u MB ==> %.2f%% ",
  1097. (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
  1098. (double)outFileSize/(double)inFileSize*100);
  1099. } }
  1100. while (1) {
  1101. int const ret = deflate(&strm, Z_FINISH);
  1102. { size_t const cSize = writeJob->bufferSize - strm.avail_out;
  1103. if (cSize) {
  1104. writeJob->usedBufferSize = cSize;
  1105. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1106. outFileSize += cSize;
  1107. strm.next_out = (Bytef*)writeJob->buffer;
  1108. strm.avail_out = (uInt)writeJob->bufferSize;
  1109. } }
  1110. if (ret == Z_STREAM_END) break;
  1111. if (ret != Z_BUF_ERROR)
  1112. EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
  1113. }
  1114. { int const ret = deflateEnd(&strm);
  1115. if (ret != Z_OK) {
  1116. EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
  1117. } }
  1118. *readsize = inFileSize;
  1119. AIO_WritePool_releaseIoJob(writeJob);
  1120. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  1121. return outFileSize;
  1122. }
  1123. #endif
  1124. #ifdef ZSTD_LZMACOMPRESS
  1125. static unsigned long long
  1126. FIO_compressLzmaFrame(cRess_t* ress,
  1127. const char* srcFileName, U64 const srcFileSize,
  1128. int compressionLevel, U64* readsize, int plain_lzma)
  1129. {
  1130. unsigned long long inFileSize = 0, outFileSize = 0;
  1131. lzma_stream strm = LZMA_STREAM_INIT;
  1132. lzma_action action = LZMA_RUN;
  1133. lzma_ret ret;
  1134. IOJob_t *writeJob = NULL;
  1135. if (compressionLevel < 0) compressionLevel = 0;
  1136. if (compressionLevel > 9) compressionLevel = 9;
  1137. if (plain_lzma) {
  1138. lzma_options_lzma opt_lzma;
  1139. if (lzma_lzma_preset(&opt_lzma, compressionLevel))
  1140. EXM_THROW(81, "zstd: %s: lzma_lzma_preset error", srcFileName);
  1141. ret = lzma_alone_encoder(&strm, &opt_lzma); /* LZMA */
  1142. if (ret != LZMA_OK)
  1143. EXM_THROW(82, "zstd: %s: lzma_alone_encoder error %d", srcFileName, ret);
  1144. } else {
  1145. ret = lzma_easy_encoder(&strm, compressionLevel, LZMA_CHECK_CRC64); /* XZ */
  1146. if (ret != LZMA_OK)
  1147. EXM_THROW(83, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
  1148. }
  1149. writeJob =AIO_WritePool_acquireJob(ress->writeCtx);
  1150. strm.next_out = (BYTE*)writeJob->buffer;
  1151. strm.avail_out = writeJob->bufferSize;
  1152. strm.next_in = 0;
  1153. strm.avail_in = 0;
  1154. while (1) {
  1155. if (strm.avail_in == 0) {
  1156. size_t const inSize = AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
  1157. if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
  1158. inFileSize += inSize;
  1159. strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
  1160. strm.avail_in = ress->readCtx->srcBufferLoaded;
  1161. }
  1162. {
  1163. size_t const availBefore = strm.avail_in;
  1164. ret = lzma_code(&strm, action);
  1165. AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
  1166. }
  1167. if (ret != LZMA_OK && ret != LZMA_STREAM_END)
  1168. EXM_THROW(84, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
  1169. { size_t const compBytes = writeJob->bufferSize - strm.avail_out;
  1170. if (compBytes) {
  1171. writeJob->usedBufferSize = compBytes;
  1172. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1173. outFileSize += compBytes;
  1174. strm.next_out = (BYTE*)writeJob->buffer;
  1175. strm.avail_out = writeJob->bufferSize;
  1176. } }
  1177. if (srcFileSize == UTIL_FILESIZE_UNKNOWN)
  1178. DISPLAYUPDATE_PROGRESS("\rRead : %u MB ==> %.2f%%",
  1179. (unsigned)(inFileSize>>20),
  1180. (double)outFileSize/(double)inFileSize*100)
  1181. else
  1182. DISPLAYUPDATE_PROGRESS("\rRead : %u / %u MB ==> %.2f%%",
  1183. (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
  1184. (double)outFileSize/(double)inFileSize*100);
  1185. if (ret == LZMA_STREAM_END) break;
  1186. }
  1187. lzma_end(&strm);
  1188. *readsize = inFileSize;
  1189. AIO_WritePool_releaseIoJob(writeJob);
  1190. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  1191. return outFileSize;
  1192. }
  1193. #endif
  1194. #ifdef ZSTD_LZ4COMPRESS
  1195. #if LZ4_VERSION_NUMBER <= 10600
  1196. #define LZ4F_blockLinked blockLinked
  1197. #define LZ4F_max64KB max64KB
  1198. #endif
  1199. static int FIO_LZ4_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
  1200. static unsigned long long
  1201. FIO_compressLz4Frame(cRess_t* ress,
  1202. const char* srcFileName, U64 const srcFileSize,
  1203. int compressionLevel, int checksumFlag,
  1204. U64* readsize)
  1205. {
  1206. const size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max64KB);
  1207. unsigned long long inFileSize = 0, outFileSize = 0;
  1208. LZ4F_preferences_t prefs;
  1209. LZ4F_compressionContext_t ctx;
  1210. IOJob_t* writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  1211. LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
  1212. if (LZ4F_isError(errorCode))
  1213. EXM_THROW(31, "zstd: failed to create lz4 compression context");
  1214. memset(&prefs, 0, sizeof(prefs));
  1215. assert(blockSize <= ress->readCtx->base.jobBufferSize);
  1216. /* autoflush off to mitigate a bug in lz4<=1.9.3 for compression level 12 */
  1217. prefs.autoFlush = 0;
  1218. prefs.compressionLevel = compressionLevel;
  1219. prefs.frameInfo.blockMode = LZ4F_blockLinked;
  1220. prefs.frameInfo.blockSizeID = LZ4F_max64KB;
  1221. prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)checksumFlag;
  1222. #if LZ4_VERSION_NUMBER >= 10600
  1223. prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize;
  1224. #endif
  1225. assert(LZ4F_compressBound(blockSize, &prefs) <= writeJob->bufferSize);
  1226. {
  1227. size_t headerSize = LZ4F_compressBegin(ctx, writeJob->buffer, writeJob->bufferSize, &prefs);
  1228. if (LZ4F_isError(headerSize))
  1229. EXM_THROW(33, "File header generation failed : %s",
  1230. LZ4F_getErrorName(headerSize));
  1231. writeJob->usedBufferSize = headerSize;
  1232. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1233. outFileSize += headerSize;
  1234. /* Read first block */
  1235. inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
  1236. /* Main Loop */
  1237. while (ress->readCtx->srcBufferLoaded) {
  1238. size_t inSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
  1239. size_t const outSize = LZ4F_compressUpdate(ctx, writeJob->buffer, writeJob->bufferSize,
  1240. ress->readCtx->srcBuffer, inSize, NULL);
  1241. if (LZ4F_isError(outSize))
  1242. EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
  1243. srcFileName, LZ4F_getErrorName(outSize));
  1244. outFileSize += outSize;
  1245. if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
  1246. DISPLAYUPDATE_PROGRESS("\rRead : %u MB ==> %.2f%%",
  1247. (unsigned)(inFileSize>>20),
  1248. (double)outFileSize/(double)inFileSize*100)
  1249. } else {
  1250. DISPLAYUPDATE_PROGRESS("\rRead : %u / %u MB ==> %.2f%%",
  1251. (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
  1252. (double)outFileSize/(double)inFileSize*100);
  1253. }
  1254. /* Write Block */
  1255. writeJob->usedBufferSize = outSize;
  1256. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1257. /* Read next block */
  1258. AIO_ReadPool_consumeBytes(ress->readCtx, inSize);
  1259. inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
  1260. }
  1261. /* End of Stream mark */
  1262. headerSize = LZ4F_compressEnd(ctx, writeJob->buffer, writeJob->bufferSize, NULL);
  1263. if (LZ4F_isError(headerSize))
  1264. EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
  1265. srcFileName, LZ4F_getErrorName(headerSize));
  1266. writeJob->usedBufferSize = headerSize;
  1267. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1268. outFileSize += headerSize;
  1269. }
  1270. *readsize = inFileSize;
  1271. LZ4F_freeCompressionContext(ctx);
  1272. AIO_WritePool_releaseIoJob(writeJob);
  1273. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  1274. return outFileSize;
  1275. }
  1276. #endif
  1277. static unsigned long long
  1278. FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
  1279. FIO_prefs_t* const prefs,
  1280. const cRess_t* ressPtr,
  1281. const char* srcFileName, U64 fileSize,
  1282. int compressionLevel, U64* readsize)
  1283. {
  1284. cRess_t const ress = *ressPtr;
  1285. IOJob_t *writeJob = AIO_WritePool_acquireJob(ressPtr->writeCtx);
  1286. U64 compressedfilesize = 0;
  1287. ZSTD_EndDirective directive = ZSTD_e_continue;
  1288. U64 pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
  1289. /* stats */
  1290. ZSTD_frameProgression previous_zfp_update = { 0, 0, 0, 0, 0, 0 };
  1291. ZSTD_frameProgression previous_zfp_correction = { 0, 0, 0, 0, 0, 0 };
  1292. typedef enum { noChange, slower, faster } speedChange_e;
  1293. speedChange_e speedChange = noChange;
  1294. unsigned flushWaiting = 0;
  1295. unsigned inputPresented = 0;
  1296. unsigned inputBlocked = 0;
  1297. unsigned lastJobID = 0;
  1298. UTIL_time_t lastAdaptTime = UTIL_getTime();
  1299. U64 const adaptEveryMicro = REFRESH_RATE;
  1300. UTIL_HumanReadableSize_t const file_hrs = UTIL_makeHumanReadableSize(fileSize);
  1301. DISPLAYLEVEL(6, "compression using zstd format \n");
  1302. /* init */
  1303. if (fileSize != UTIL_FILESIZE_UNKNOWN) {
  1304. pledgedSrcSize = fileSize;
  1305. CHECK(ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize));
  1306. } else if (prefs->streamSrcSize > 0) {
  1307. /* unknown source size; use the declared stream size */
  1308. pledgedSrcSize = prefs->streamSrcSize;
  1309. CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, prefs->streamSrcSize) );
  1310. }
  1311. {
  1312. int windowLog;
  1313. UTIL_HumanReadableSize_t windowSize;
  1314. CHECK(ZSTD_CCtx_getParameter(ress.cctx, ZSTD_c_windowLog, &windowLog));
  1315. if (windowLog == 0) {
  1316. if (prefs->ldmFlag) {
  1317. /* If long mode is set without a window size libzstd will set this size internally */
  1318. windowLog = ZSTD_WINDOWLOG_LIMIT_DEFAULT;
  1319. } else {
  1320. const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
  1321. windowLog = (int)cParams.windowLog;
  1322. }
  1323. }
  1324. windowSize = UTIL_makeHumanReadableSize(MAX(1ULL, MIN(1ULL << windowLog, pledgedSrcSize)));
  1325. DISPLAYLEVEL(4, "Decompression will require %.*f%s of memory\n", windowSize.precision, windowSize.value, windowSize.suffix);
  1326. }
  1327. (void)srcFileName;
  1328. /* Main compression loop */
  1329. do {
  1330. size_t stillToFlush;
  1331. /* Fill input Buffer */
  1332. size_t const inSize = AIO_ReadPool_fillBuffer(ress.readCtx, ZSTD_CStreamInSize());
  1333. ZSTD_inBuffer inBuff = setInBuffer( ress.readCtx->srcBuffer, ress.readCtx->srcBufferLoaded, 0 );
  1334. DISPLAYLEVEL(6, "fread %u bytes from source \n", (unsigned)inSize);
  1335. *readsize += inSize;
  1336. if ((ress.readCtx->srcBufferLoaded == 0) || (*readsize == fileSize))
  1337. directive = ZSTD_e_end;
  1338. stillToFlush = 1;
  1339. while ((inBuff.pos != inBuff.size) /* input buffer must be entirely ingested */
  1340. || (directive == ZSTD_e_end && stillToFlush != 0) ) {
  1341. size_t const oldIPos = inBuff.pos;
  1342. ZSTD_outBuffer outBuff = setOutBuffer( writeJob->buffer, writeJob->bufferSize, 0 );
  1343. size_t const toFlushNow = ZSTD_toFlushNow(ress.cctx);
  1344. CHECK_V(stillToFlush, ZSTD_compressStream2(ress.cctx, &outBuff, &inBuff, directive));
  1345. AIO_ReadPool_consumeBytes(ress.readCtx, inBuff.pos - oldIPos);
  1346. /* count stats */
  1347. inputPresented++;
  1348. if (oldIPos == inBuff.pos) inputBlocked++; /* input buffer is full and can't take any more : input speed is faster than consumption rate */
  1349. if (!toFlushNow) flushWaiting = 1;
  1350. /* Write compressed stream */
  1351. DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => input pos(%u)<=(%u)size ; output generated %u bytes \n",
  1352. (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
  1353. if (outBuff.pos) {
  1354. writeJob->usedBufferSize = outBuff.pos;
  1355. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1356. compressedfilesize += outBuff.pos;
  1357. }
  1358. /* adaptive mode : statistics measurement and speed correction */
  1359. if (prefs->adaptiveMode && UTIL_clockSpanMicro(lastAdaptTime) > adaptEveryMicro) {
  1360. ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
  1361. lastAdaptTime = UTIL_getTime();
  1362. /* check output speed */
  1363. if (zfp.currentJobID > 1) { /* only possible if nbWorkers >= 1 */
  1364. unsigned long long newlyProduced = zfp.produced - previous_zfp_update.produced;
  1365. unsigned long long newlyFlushed = zfp.flushed - previous_zfp_update.flushed;
  1366. assert(zfp.produced >= previous_zfp_update.produced);
  1367. assert(prefs->nbWorkers >= 1);
  1368. /* test if compression is blocked
  1369. * either because output is slow and all buffers are full
  1370. * or because input is slow and no job can start while waiting for at least one buffer to be filled.
  1371. * note : exclude starting part, since currentJobID > 1 */
  1372. if ( (zfp.consumed == previous_zfp_update.consumed) /* no data compressed : no data available, or no more buffer to compress to, OR compression is really slow (compression of a single block is slower than update rate)*/
  1373. && (zfp.nbActiveWorkers == 0) /* confirmed : no compression ongoing */
  1374. ) {
  1375. DISPLAYLEVEL(6, "all buffers full : compression stopped => slow down \n")
  1376. speedChange = slower;
  1377. }
  1378. previous_zfp_update = zfp;
  1379. if ( (newlyProduced > (newlyFlushed * 9 / 8)) /* compression produces more data than output can flush (though production can be spiky, due to work unit : (N==4)*block sizes) */
  1380. && (flushWaiting == 0) /* flush speed was never slowed by lack of production, so it's operating at max capacity */
  1381. ) {
  1382. DISPLAYLEVEL(6, "compression faster than flush (%llu > %llu), and flushed was never slowed down by lack of production => slow down \n", newlyProduced, newlyFlushed);
  1383. speedChange = slower;
  1384. }
  1385. flushWaiting = 0;
  1386. }
  1387. /* course correct only if there is at least one new job completed */
  1388. if (zfp.currentJobID > lastJobID) {
  1389. DISPLAYLEVEL(6, "compression level adaptation check \n")
  1390. /* check input speed */
  1391. if (zfp.currentJobID > (unsigned)(prefs->nbWorkers+1)) { /* warm up period, to fill all workers */
  1392. if (inputBlocked <= 0) {
  1393. DISPLAYLEVEL(6, "input is never blocked => input is slower than ingestion \n");
  1394. speedChange = slower;
  1395. } else if (speedChange == noChange) {
  1396. unsigned long long newlyIngested = zfp.ingested - previous_zfp_correction.ingested;
  1397. unsigned long long newlyConsumed = zfp.consumed - previous_zfp_correction.consumed;
  1398. unsigned long long newlyProduced = zfp.produced - previous_zfp_correction.produced;
  1399. unsigned long long newlyFlushed = zfp.flushed - previous_zfp_correction.flushed;
  1400. previous_zfp_correction = zfp;
  1401. assert(inputPresented > 0);
  1402. DISPLAYLEVEL(6, "input blocked %u/%u(%.2f) - ingested:%u vs %u:consumed - flushed:%u vs %u:produced \n",
  1403. inputBlocked, inputPresented, (double)inputBlocked/inputPresented*100,
  1404. (unsigned)newlyIngested, (unsigned)newlyConsumed,
  1405. (unsigned)newlyFlushed, (unsigned)newlyProduced);
  1406. if ( (inputBlocked > inputPresented / 8) /* input is waiting often, because input buffers is full : compression or output too slow */
  1407. && (newlyFlushed * 33 / 32 > newlyProduced) /* flush everything that is produced */
  1408. && (newlyIngested * 33 / 32 > newlyConsumed) /* input speed as fast or faster than compression speed */
  1409. ) {
  1410. DISPLAYLEVEL(6, "recommend faster as in(%llu) >= (%llu)comp(%llu) <= out(%llu) \n",
  1411. newlyIngested, newlyConsumed, newlyProduced, newlyFlushed);
  1412. speedChange = faster;
  1413. }
  1414. }
  1415. inputBlocked = 0;
  1416. inputPresented = 0;
  1417. }
  1418. if (speedChange == slower) {
  1419. DISPLAYLEVEL(6, "slower speed , higher compression \n")
  1420. compressionLevel ++;
  1421. if (compressionLevel > ZSTD_maxCLevel()) compressionLevel = ZSTD_maxCLevel();
  1422. if (compressionLevel > prefs->maxAdaptLevel) compressionLevel = prefs->maxAdaptLevel;
  1423. compressionLevel += (compressionLevel == 0); /* skip 0 */
  1424. ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
  1425. }
  1426. if (speedChange == faster) {
  1427. DISPLAYLEVEL(6, "faster speed , lighter compression \n")
  1428. compressionLevel --;
  1429. if (compressionLevel < prefs->minAdaptLevel) compressionLevel = prefs->minAdaptLevel;
  1430. compressionLevel -= (compressionLevel == 0); /* skip 0 */
  1431. ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
  1432. }
  1433. speedChange = noChange;
  1434. lastJobID = zfp.currentJobID;
  1435. } /* if (zfp.currentJobID > lastJobID) */
  1436. } /* if (prefs->adaptiveMode && UTIL_clockSpanMicro(lastAdaptTime) > adaptEveryMicro) */
  1437. /* display notification */
  1438. if (SHOULD_DISPLAY_PROGRESS() && READY_FOR_UPDATE()) {
  1439. ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
  1440. double const cShare = (double)zfp.produced / (double)(zfp.consumed + !zfp.consumed/*avoid div0*/) * 100;
  1441. UTIL_HumanReadableSize_t const buffered_hrs = UTIL_makeHumanReadableSize(zfp.ingested - zfp.consumed);
  1442. UTIL_HumanReadableSize_t const consumed_hrs = UTIL_makeHumanReadableSize(zfp.consumed);
  1443. UTIL_HumanReadableSize_t const produced_hrs = UTIL_makeHumanReadableSize(zfp.produced);
  1444. DELAY_NEXT_UPDATE();
  1445. /* display progress notifications */
  1446. DISPLAY_PROGRESS("\r%79s\r", ""); /* Clear out the current displayed line */
  1447. if (g_display_prefs.displayLevel >= 3) {
  1448. /* Verbose progress update */
  1449. DISPLAY_PROGRESS(
  1450. "(L%i) Buffered:%5.*f%s - Consumed:%5.*f%s - Compressed:%5.*f%s => %.2f%% ",
  1451. compressionLevel,
  1452. buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
  1453. consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
  1454. produced_hrs.precision, produced_hrs.value, produced_hrs.suffix,
  1455. cShare );
  1456. } else {
  1457. /* Require level 2 or forcibly displayed progress counter for summarized updates */
  1458. if (fCtx->nbFilesTotal > 1) {
  1459. size_t srcFileNameSize = strlen(srcFileName);
  1460. /* Ensure that the string we print is roughly the same size each time */
  1461. if (srcFileNameSize > 18) {
  1462. const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
  1463. DISPLAY_PROGRESS("Compress: %u/%u files. Current: ...%s ",
  1464. fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName);
  1465. } else {
  1466. DISPLAY_PROGRESS("Compress: %u/%u files. Current: %*s ",
  1467. fCtx->currFileIdx+1, fCtx->nbFilesTotal, (int)(18-srcFileNameSize), srcFileName);
  1468. }
  1469. }
  1470. DISPLAY_PROGRESS("Read:%6.*f%4s ", consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix);
  1471. if (fileSize != UTIL_FILESIZE_UNKNOWN)
  1472. DISPLAY_PROGRESS("/%6.*f%4s", file_hrs.precision, file_hrs.value, file_hrs.suffix);
  1473. DISPLAY_PROGRESS(" ==> %2.f%%", cShare);
  1474. }
  1475. } /* if (SHOULD_DISPLAY_PROGRESS() && READY_FOR_UPDATE()) */
  1476. } /* while ((inBuff.pos != inBuff.size) */
  1477. } while (directive != ZSTD_e_end);
  1478. if (fileSize != UTIL_FILESIZE_UNKNOWN && *readsize != fileSize) {
  1479. EXM_THROW(27, "Read error : Incomplete read : %llu / %llu B",
  1480. (unsigned long long)*readsize, (unsigned long long)fileSize);
  1481. }
  1482. AIO_WritePool_releaseIoJob(writeJob);
  1483. AIO_WritePool_sparseWriteEnd(ressPtr->writeCtx);
  1484. return compressedfilesize;
  1485. }
  1486. /*! FIO_compressFilename_internal() :
  1487. * same as FIO_compressFilename_extRess(), with `ress.desFile` already opened.
  1488. * @return : 0 : compression completed correctly,
  1489. * 1 : missing or pb opening srcFileName
  1490. */
  1491. static int
  1492. FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
  1493. FIO_prefs_t* const prefs,
  1494. cRess_t ress,
  1495. const char* dstFileName, const char* srcFileName,
  1496. int compressionLevel)
  1497. {
  1498. UTIL_time_t const timeStart = UTIL_getTime();
  1499. clock_t const cpuStart = clock();
  1500. U64 readsize = 0;
  1501. U64 compressedfilesize = 0;
  1502. U64 const fileSize = UTIL_getFileSize(srcFileName);
  1503. DISPLAYLEVEL(5, "%s: %llu bytes \n", srcFileName, (unsigned long long)fileSize);
  1504. /* compression format selection */
  1505. switch (prefs->compressionType) {
  1506. default:
  1507. case FIO_zstdCompression:
  1508. compressedfilesize = FIO_compressZstdFrame(fCtx, prefs, &ress, srcFileName, fileSize, compressionLevel, &readsize);
  1509. break;
  1510. case FIO_gzipCompression:
  1511. #ifdef ZSTD_GZCOMPRESS
  1512. compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize);
  1513. #else
  1514. (void)compressionLevel;
  1515. EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n",
  1516. srcFileName);
  1517. #endif
  1518. break;
  1519. case FIO_xzCompression:
  1520. case FIO_lzmaCompression:
  1521. #ifdef ZSTD_LZMACOMPRESS
  1522. compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, prefs->compressionType==FIO_lzmaCompression);
  1523. #else
  1524. (void)compressionLevel;
  1525. EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n",
  1526. srcFileName);
  1527. #endif
  1528. break;
  1529. case FIO_lz4Compression:
  1530. #ifdef ZSTD_LZ4COMPRESS
  1531. compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, prefs->checksumFlag, &readsize);
  1532. #else
  1533. (void)compressionLevel;
  1534. EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n",
  1535. srcFileName);
  1536. #endif
  1537. break;
  1538. }
  1539. /* Status */
  1540. fCtx->totalBytesInput += (size_t)readsize;
  1541. fCtx->totalBytesOutput += (size_t)compressedfilesize;
  1542. DISPLAY_PROGRESS("\r%79s\r", "");
  1543. if (FIO_shouldDisplayFileSummary(fCtx)) {
  1544. UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) readsize);
  1545. UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) compressedfilesize);
  1546. if (readsize == 0) {
  1547. DISPLAY_SUMMARY("%-20s : (%6.*f%s => %6.*f%s, %s) \n",
  1548. srcFileName,
  1549. hr_isize.precision, hr_isize.value, hr_isize.suffix,
  1550. hr_osize.precision, hr_osize.value, hr_osize.suffix,
  1551. dstFileName);
  1552. } else {
  1553. DISPLAY_SUMMARY("%-20s :%6.2f%% (%6.*f%s => %6.*f%s, %s) \n",
  1554. srcFileName,
  1555. (double)compressedfilesize / (double)readsize * 100,
  1556. hr_isize.precision, hr_isize.value, hr_isize.suffix,
  1557. hr_osize.precision, hr_osize.value, hr_osize.suffix,
  1558. dstFileName);
  1559. }
  1560. }
  1561. /* Elapsed Time and CPU Load */
  1562. { clock_t const cpuEnd = clock();
  1563. double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
  1564. U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
  1565. double const timeLength_s = (double)timeLength_ns / 1000000000;
  1566. double const cpuLoad_pct = (cpuLoad_s / timeLength_s) * 100;
  1567. DISPLAYLEVEL(4, "%-20s : Completed in %.2f sec (cpu load : %.0f%%)\n",
  1568. srcFileName, timeLength_s, cpuLoad_pct);
  1569. }
  1570. return 0;
  1571. }
  1572. /*! FIO_compressFilename_dstFile() :
  1573. * open dstFileName, or pass-through if ress.file != NULL,
  1574. * then start compression with FIO_compressFilename_internal().
  1575. * Manages source removal (--rm) and file permissions transfer.
  1576. * note : ress.srcFile must be != NULL,
  1577. * so reach this function through FIO_compressFilename_srcFile().
  1578. * @return : 0 : compression completed correctly,
  1579. * 1 : pb
  1580. */
  1581. static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
  1582. FIO_prefs_t* const prefs,
  1583. cRess_t ress,
  1584. const char* dstFileName,
  1585. const char* srcFileName,
  1586. const stat_t* srcFileStat,
  1587. int compressionLevel)
  1588. {
  1589. int closeDstFile = 0;
  1590. int result;
  1591. int transferStat = 0;
  1592. FILE *dstFile;
  1593. int dstFd = -1;
  1594. assert(AIO_ReadPool_getFile(ress.readCtx) != NULL);
  1595. if (AIO_WritePool_getFile(ress.writeCtx) == NULL) {
  1596. int dstFileInitialPermissions = DEFAULT_FILE_PERMISSIONS;
  1597. if ( strcmp (srcFileName, stdinmark)
  1598. && strcmp (dstFileName, stdoutmark)
  1599. && UTIL_isRegularFileStat(srcFileStat) ) {
  1600. transferStat = 1;
  1601. dstFileInitialPermissions = TEMPORARY_FILE_PERMISSIONS;
  1602. }
  1603. closeDstFile = 1;
  1604. DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName);
  1605. dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFileInitialPermissions);
  1606. if (dstFile==NULL) return 1; /* could not open dstFileName */
  1607. dstFd = fileno(dstFile);
  1608. AIO_WritePool_setFile(ress.writeCtx, dstFile);
  1609. /* Must only be added after FIO_openDstFile() succeeds.
  1610. * Otherwise we may delete the destination file if it already exists,
  1611. * and the user presses Ctrl-C when asked if they wish to overwrite.
  1612. */
  1613. addHandler(dstFileName);
  1614. }
  1615. result = FIO_compressFilename_internal(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
  1616. if (closeDstFile) {
  1617. clearHandler();
  1618. if (transferStat) {
  1619. UTIL_setFDStat(dstFd, dstFileName, srcFileStat);
  1620. }
  1621. DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName);
  1622. if (AIO_WritePool_closeFile(ress.writeCtx)) { /* error closing file */
  1623. DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
  1624. result=1;
  1625. }
  1626. if (transferStat) {
  1627. UTIL_utime(dstFileName, srcFileStat);
  1628. }
  1629. if ( (result != 0) /* operation failure */
  1630. && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */
  1631. ) {
  1632. FIO_removeFile(dstFileName); /* remove compression artefact; note don't do anything special if remove() fails */
  1633. }
  1634. }
  1635. return result;
  1636. }
  1637. /* List used to compare file extensions (used with --exclude-compressed flag)
  1638. * Different from the suffixList and should only apply to ZSTD compress operationResult
  1639. */
  1640. static const char *compressedFileExtensions[] = {
  1641. ZSTD_EXTENSION,
  1642. TZSTD_EXTENSION,
  1643. GZ_EXTENSION,
  1644. TGZ_EXTENSION,
  1645. LZMA_EXTENSION,
  1646. XZ_EXTENSION,
  1647. TXZ_EXTENSION,
  1648. LZ4_EXTENSION,
  1649. TLZ4_EXTENSION,
  1650. NULL
  1651. };
  1652. /*! FIO_compressFilename_srcFile() :
  1653. * @return : 0 : compression completed correctly,
  1654. * 1 : missing or pb opening srcFileName
  1655. */
  1656. static int
  1657. FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
  1658. FIO_prefs_t* const prefs,
  1659. cRess_t ress,
  1660. const char* dstFileName,
  1661. const char* srcFileName,
  1662. int compressionLevel)
  1663. {
  1664. int result;
  1665. FILE* srcFile;
  1666. stat_t srcFileStat;
  1667. U64 fileSize = UTIL_FILESIZE_UNKNOWN;
  1668. DISPLAYLEVEL(6, "FIO_compressFilename_srcFile: %s \n", srcFileName);
  1669. if (strcmp(srcFileName, stdinmark)) {
  1670. if (UTIL_stat(srcFileName, &srcFileStat)) {
  1671. /* failure to stat at all is handled during opening */
  1672. /* ensure src is not a directory */
  1673. if (UTIL_isDirectoryStat(&srcFileStat)) {
  1674. DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
  1675. return 1;
  1676. }
  1677. /* ensure src is not the same as dict (if present) */
  1678. if (ress.dictFileName != NULL && UTIL_isSameFileStat(srcFileName, ress.dictFileName, &srcFileStat, &ress.dictFileStat)) {
  1679. DISPLAYLEVEL(1, "zstd: cannot use %s as an input file and dictionary \n", srcFileName);
  1680. return 1;
  1681. }
  1682. }
  1683. }
  1684. /* Check if "srcFile" is compressed. Only done if --exclude-compressed flag is used
  1685. * YES => ZSTD will skip compression of the file and will return 0.
  1686. * NO => ZSTD will resume with compress operation.
  1687. */
  1688. if (prefs->excludeCompressedFiles == 1 && UTIL_isCompressedFile(srcFileName, compressedFileExtensions)) {
  1689. DISPLAYLEVEL(4, "File is already compressed : %s \n", srcFileName);
  1690. return 0;
  1691. }
  1692. srcFile = FIO_openSrcFile(prefs, srcFileName, &srcFileStat);
  1693. if (srcFile == NULL) return 1; /* srcFile could not be opened */
  1694. /* Don't use AsyncIO for small files */
  1695. if (strcmp(srcFileName, stdinmark)) /* Stdin doesn't have stats */
  1696. fileSize = UTIL_getFileSizeStat(&srcFileStat);
  1697. if(fileSize != UTIL_FILESIZE_UNKNOWN && fileSize < ZSTD_BLOCKSIZE_MAX * 3) {
  1698. AIO_ReadPool_setAsync(ress.readCtx, 0);
  1699. AIO_WritePool_setAsync(ress.writeCtx, 0);
  1700. } else {
  1701. AIO_ReadPool_setAsync(ress.readCtx, 1);
  1702. AIO_WritePool_setAsync(ress.writeCtx, 1);
  1703. }
  1704. AIO_ReadPool_setFile(ress.readCtx, srcFile);
  1705. result = FIO_compressFilename_dstFile(
  1706. fCtx, prefs, ress,
  1707. dstFileName, srcFileName,
  1708. &srcFileStat, compressionLevel);
  1709. AIO_ReadPool_closeFile(ress.readCtx);
  1710. if ( prefs->removeSrcFile /* --rm */
  1711. && result == 0 /* success */
  1712. && strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
  1713. ) {
  1714. /* We must clear the handler, since after this point calling it would
  1715. * delete both the source and destination files.
  1716. */
  1717. clearHandler();
  1718. if (FIO_removeFile(srcFileName))
  1719. EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno));
  1720. }
  1721. return result;
  1722. }
  1723. static const char*
  1724. checked_index(const char* options[], size_t length, size_t index) {
  1725. assert(index < length);
  1726. /* Necessary to avoid warnings since -O3 will omit the above `assert` */
  1727. (void) length;
  1728. return options[index];
  1729. }
  1730. #define INDEX(options, index) checked_index((options), sizeof(options) / sizeof(char*), (size_t)(index))
  1731. void FIO_displayCompressionParameters(const FIO_prefs_t* prefs)
  1732. {
  1733. static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
  1734. LZMA_EXTENSION, LZ4_EXTENSION};
  1735. static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
  1736. static const char* checkSumOptions[3] = {" --no-check", "", " --check"};
  1737. static const char* rowMatchFinderOptions[3] = {"", " --no-row-match-finder", " --row-match-finder"};
  1738. static const char* compressLiteralsOptions[3] = {"", " --compress-literals", " --no-compress-literals"};
  1739. assert(g_display_prefs.displayLevel >= 4);
  1740. DISPLAY("--format=%s", formatOptions[prefs->compressionType]);
  1741. DISPLAY("%s", INDEX(sparseOptions, prefs->sparseFileSupport));
  1742. DISPLAY("%s", prefs->dictIDFlag ? "" : " --no-dictID");
  1743. DISPLAY("%s", INDEX(checkSumOptions, prefs->checksumFlag));
  1744. DISPLAY(" --block-size=%d", prefs->blockSize);
  1745. if (prefs->adaptiveMode)
  1746. DISPLAY(" --adapt=min=%d,max=%d", prefs->minAdaptLevel, prefs->maxAdaptLevel);
  1747. DISPLAY("%s", INDEX(rowMatchFinderOptions, prefs->useRowMatchFinder));
  1748. DISPLAY("%s", prefs->rsyncable ? " --rsyncable" : "");
  1749. if (prefs->streamSrcSize)
  1750. DISPLAY(" --stream-size=%u", (unsigned) prefs->streamSrcSize);
  1751. if (prefs->srcSizeHint)
  1752. DISPLAY(" --size-hint=%d", prefs->srcSizeHint);
  1753. if (prefs->targetCBlockSize)
  1754. DISPLAY(" --target-compressed-block-size=%u", (unsigned) prefs->targetCBlockSize);
  1755. DISPLAY("%s", INDEX(compressLiteralsOptions, prefs->literalCompressionMode));
  1756. DISPLAY(" --memory=%u", prefs->memLimit ? prefs->memLimit : 128 MB);
  1757. DISPLAY(" --threads=%d", prefs->nbWorkers);
  1758. DISPLAY("%s", prefs->excludeCompressedFiles ? " --exclude-compressed" : "");
  1759. DISPLAY(" --%scontent-size", prefs->contentSize ? "" : "no-");
  1760. DISPLAY("\n");
  1761. }
  1762. #undef INDEX
  1763. int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const char* dstFileName,
  1764. const char* srcFileName, const char* dictFileName,
  1765. int compressionLevel, ZSTD_compressionParameters comprParams)
  1766. {
  1767. cRess_t ress = FIO_createCResources(prefs, dictFileName, UTIL_getFileSize(srcFileName), compressionLevel, comprParams);
  1768. int const result = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
  1769. #define DISPLAY_LEVEL_DEFAULT 2
  1770. FIO_freeCResources(&ress);
  1771. return result;
  1772. }
  1773. /* FIO_determineCompressedName() :
  1774. * create a destination filename for compressed srcFileName.
  1775. * @return a pointer to it.
  1776. * This function never returns an error (it may abort() in case of pb)
  1777. */
  1778. static const char*
  1779. FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
  1780. {
  1781. static size_t dfnbCapacity = 0;
  1782. static char* dstFileNameBuffer = NULL; /* using static allocation : this function cannot be multi-threaded */
  1783. char* outDirFilename = NULL;
  1784. size_t sfnSize = strlen(srcFileName);
  1785. size_t const srcSuffixLen = strlen(suffix);
  1786. if(!strcmp(srcFileName, stdinmark)) {
  1787. return stdoutmark;
  1788. }
  1789. if (outDirName) {
  1790. outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
  1791. sfnSize = strlen(outDirFilename);
  1792. assert(outDirFilename != NULL);
  1793. }
  1794. if (dfnbCapacity <= sfnSize+srcSuffixLen+1) {
  1795. /* resize buffer for dstName */
  1796. free(dstFileNameBuffer);
  1797. dfnbCapacity = sfnSize + srcSuffixLen + 30;
  1798. dstFileNameBuffer = (char*)malloc(dfnbCapacity);
  1799. if (!dstFileNameBuffer) {
  1800. EXM_THROW(30, "zstd: %s", strerror(errno));
  1801. }
  1802. }
  1803. assert(dstFileNameBuffer != NULL);
  1804. if (outDirFilename) {
  1805. memcpy(dstFileNameBuffer, outDirFilename, sfnSize);
  1806. free(outDirFilename);
  1807. } else {
  1808. memcpy(dstFileNameBuffer, srcFileName, sfnSize);
  1809. }
  1810. memcpy(dstFileNameBuffer+sfnSize, suffix, srcSuffixLen+1 /* Include terminating null */);
  1811. return dstFileNameBuffer;
  1812. }
  1813. static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
  1814. {
  1815. size_t i;
  1816. unsigned long long fileSize, maxFileSize = 0;
  1817. for (i = 0; i < nbFiles; i++) {
  1818. fileSize = UTIL_getFileSize(inFileNames[i]);
  1819. maxFileSize = fileSize > maxFileSize ? fileSize : maxFileSize;
  1820. }
  1821. return maxFileSize;
  1822. }
  1823. /* FIO_compressMultipleFilenames() :
  1824. * compress nbFiles files
  1825. * into either one destination (outFileName),
  1826. * or into one file each (outFileName == NULL, but suffix != NULL),
  1827. * or into a destination folder (specified with -O)
  1828. */
  1829. int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
  1830. FIO_prefs_t* const prefs,
  1831. const char** inFileNamesTable,
  1832. const char* outMirroredRootDirName,
  1833. const char* outDirName,
  1834. const char* outFileName, const char* suffix,
  1835. const char* dictFileName, int compressionLevel,
  1836. ZSTD_compressionParameters comprParams)
  1837. {
  1838. int status;
  1839. int error = 0;
  1840. cRess_t ress = FIO_createCResources(prefs, dictFileName,
  1841. FIO_getLargestFileSize(inFileNamesTable, (unsigned)fCtx->nbFilesTotal),
  1842. compressionLevel, comprParams);
  1843. /* init */
  1844. assert(outFileName != NULL || suffix != NULL);
  1845. if (outFileName != NULL) { /* output into a single destination (stdout typically) */
  1846. FILE *dstFile;
  1847. if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
  1848. FIO_freeCResources(&ress);
  1849. return 1;
  1850. }
  1851. dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
  1852. if (dstFile == NULL) { /* could not open outFileName */
  1853. error = 1;
  1854. } else {
  1855. AIO_WritePool_setFile(ress.writeCtx, dstFile);
  1856. for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
  1857. status = FIO_compressFilename_srcFile(fCtx, prefs, ress, outFileName, inFileNamesTable[fCtx->currFileIdx], compressionLevel);
  1858. if (!status) fCtx->nbFilesProcessed++;
  1859. error |= status;
  1860. }
  1861. if (AIO_WritePool_closeFile(ress.writeCtx))
  1862. EXM_THROW(29, "Write error (%s) : cannot properly close %s",
  1863. strerror(errno), outFileName);
  1864. }
  1865. } else {
  1866. if (outMirroredRootDirName)
  1867. UTIL_mirrorSourceFilesDirectories(inFileNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
  1868. for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
  1869. const char* const srcFileName = inFileNamesTable[fCtx->currFileIdx];
  1870. const char* dstFileName = NULL;
  1871. if (outMirroredRootDirName) {
  1872. char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
  1873. if (validMirroredDirName) {
  1874. dstFileName = FIO_determineCompressedName(srcFileName, validMirroredDirName, suffix);
  1875. free(validMirroredDirName);
  1876. } else {
  1877. DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot compress '%s' into '%s' \n", srcFileName, outMirroredRootDirName);
  1878. error=1;
  1879. continue;
  1880. }
  1881. } else {
  1882. dstFileName = FIO_determineCompressedName(srcFileName, outDirName, suffix); /* cannot fail */
  1883. }
  1884. status = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
  1885. if (!status) fCtx->nbFilesProcessed++;
  1886. error |= status;
  1887. }
  1888. if (outDirName)
  1889. FIO_checkFilenameCollisions(inFileNamesTable , (unsigned)fCtx->nbFilesTotal);
  1890. }
  1891. if (FIO_shouldDisplayMultipleFileSummary(fCtx)) {
  1892. UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesInput);
  1893. UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesOutput);
  1894. DISPLAY_PROGRESS("\r%79s\r", "");
  1895. if (fCtx->totalBytesInput == 0) {
  1896. DISPLAY_SUMMARY("%3d files compressed : (%6.*f%4s => %6.*f%4s)\n",
  1897. fCtx->nbFilesProcessed,
  1898. hr_isize.precision, hr_isize.value, hr_isize.suffix,
  1899. hr_osize.precision, hr_osize.value, hr_osize.suffix);
  1900. } else {
  1901. DISPLAY_SUMMARY("%3d files compressed : %.2f%% (%6.*f%4s => %6.*f%4s)\n",
  1902. fCtx->nbFilesProcessed,
  1903. (double)fCtx->totalBytesOutput/((double)fCtx->totalBytesInput)*100,
  1904. hr_isize.precision, hr_isize.value, hr_isize.suffix,
  1905. hr_osize.precision, hr_osize.value, hr_osize.suffix);
  1906. }
  1907. }
  1908. FIO_freeCResources(&ress);
  1909. return error;
  1910. }
  1911. #endif /* #ifndef ZSTD_NOCOMPRESS */
  1912. #ifndef ZSTD_NODECOMPRESS
  1913. /* **************************************************************************
  1914. * Decompression
  1915. ***************************************************************************/
  1916. typedef struct {
  1917. FIO_Dict_t dict;
  1918. ZSTD_DStream* dctx;
  1919. WritePoolCtx_t *writeCtx;
  1920. ReadPoolCtx_t *readCtx;
  1921. } dRess_t;
  1922. static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
  1923. {
  1924. int useMMap = prefs->mmapDict == ZSTD_ps_enable;
  1925. int forceNoUseMMap = prefs->mmapDict == ZSTD_ps_disable;
  1926. stat_t statbuf;
  1927. dRess_t ress;
  1928. memset(&ress, 0, sizeof(ress));
  1929. FIO_getDictFileStat(dictFileName, &statbuf);
  1930. if (prefs->patchFromMode){
  1931. U64 const dictSize = UTIL_getFileSizeStat(&statbuf);
  1932. useMMap |= dictSize > prefs->memLimit;
  1933. FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, 0 /* just use the dict size */);
  1934. }
  1935. /* Allocation */
  1936. ress.dctx = ZSTD_createDStream();
  1937. if (ress.dctx==NULL)
  1938. EXM_THROW(60, "Error: %s : can't create ZSTD_DStream", strerror(errno));
  1939. CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) );
  1940. CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag));
  1941. /* dictionary */
  1942. {
  1943. FIO_dictBufferType_t dictBufferType = (useMMap && !forceNoUseMMap) ? FIO_mmapDict : FIO_mallocDict;
  1944. FIO_initDict(&ress.dict, dictFileName, prefs, &statbuf, dictBufferType);
  1945. CHECK(ZSTD_DCtx_reset(ress.dctx, ZSTD_reset_session_only) );
  1946. if (prefs->patchFromMode){
  1947. CHECK(ZSTD_DCtx_refPrefix(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize));
  1948. } else {
  1949. CHECK(ZSTD_DCtx_loadDictionary_byReference(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize));
  1950. }
  1951. }
  1952. ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
  1953. ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize());
  1954. return ress;
  1955. }
  1956. static void FIO_freeDResources(dRess_t ress)
  1957. {
  1958. FIO_freeDict(&(ress.dict));
  1959. CHECK( ZSTD_freeDStream(ress.dctx) );
  1960. AIO_WritePool_free(ress.writeCtx);
  1961. AIO_ReadPool_free(ress.readCtx);
  1962. }
  1963. /* FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
  1964. * @return : 0 (no error) */
  1965. static int FIO_passThrough(dRess_t *ress)
  1966. {
  1967. size_t const blockSize = MIN(MIN(64 KB, ZSTD_DStreamInSize()), ZSTD_DStreamOutSize());
  1968. IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  1969. AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
  1970. while(ress->readCtx->srcBufferLoaded) {
  1971. size_t writeSize;
  1972. writeSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
  1973. assert(writeSize <= writeJob->bufferSize);
  1974. memcpy(writeJob->buffer, ress->readCtx->srcBuffer, writeSize);
  1975. writeJob->usedBufferSize = writeSize;
  1976. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  1977. AIO_ReadPool_consumeBytes(ress->readCtx, writeSize);
  1978. AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
  1979. }
  1980. assert(ress->readCtx->reachedEof);
  1981. AIO_WritePool_releaseIoJob(writeJob);
  1982. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  1983. return 0;
  1984. }
  1985. /* FIO_zstdErrorHelp() :
  1986. * detailed error message when requested window size is too large */
  1987. static void
  1988. FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
  1989. const dRess_t* ress,
  1990. size_t err,
  1991. const char* srcFileName)
  1992. {
  1993. ZSTD_frameHeader header;
  1994. /* Help message only for one specific error */
  1995. if (ZSTD_getErrorCode(err) != ZSTD_error_frameParameter_windowTooLarge)
  1996. return;
  1997. /* Try to decode the frame header */
  1998. err = ZSTD_getFrameHeader(&header, ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded);
  1999. if (err == 0) {
  2000. unsigned long long const windowSize = header.windowSize;
  2001. unsigned const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
  2002. assert(prefs->memLimit > 0);
  2003. DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u \n",
  2004. srcFileName, windowSize, prefs->memLimit);
  2005. if (windowLog <= ZSTD_WINDOWLOG_MAX) {
  2006. unsigned const windowMB = (unsigned)((windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0));
  2007. assert(windowSize < (U64)(1ULL << 52)); /* ensure now overflow for windowMB */
  2008. DISPLAYLEVEL(1, "%s : Use --long=%u or --memory=%uMB \n",
  2009. srcFileName, windowLog, windowMB);
  2010. return;
  2011. } }
  2012. DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u; not supported \n",
  2013. srcFileName, ZSTD_WINDOWLOG_MAX);
  2014. }
  2015. /** FIO_decompressFrame() :
  2016. * @return : size of decoded zstd frame, or an error code
  2017. */
  2018. #define FIO_ERROR_FRAME_DECODING ((unsigned long long)(-2))
  2019. static unsigned long long
  2020. FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress,
  2021. const FIO_prefs_t* const prefs,
  2022. const char* srcFileName,
  2023. U64 alreadyDecoded) /* for multi-frames streams */
  2024. {
  2025. U64 frameSize = 0;
  2026. IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  2027. /* display last 20 characters only */
  2028. { size_t const srcFileLength = strlen(srcFileName);
  2029. if (srcFileLength>20) srcFileName += srcFileLength-20;
  2030. }
  2031. ZSTD_DCtx_reset(ress->dctx, ZSTD_reset_session_only);
  2032. /* Header loading : ensures ZSTD_getFrameHeader() will succeed */
  2033. AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_FRAMEHEADERSIZE_MAX);
  2034. /* Main decompression Loop */
  2035. while (1) {
  2036. ZSTD_inBuffer inBuff = setInBuffer( ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded, 0 );
  2037. ZSTD_outBuffer outBuff= setOutBuffer( writeJob->buffer, writeJob->bufferSize, 0 );
  2038. size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
  2039. UTIL_HumanReadableSize_t const hrs = UTIL_makeHumanReadableSize(alreadyDecoded+frameSize);
  2040. if (ZSTD_isError(readSizeHint)) {
  2041. DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
  2042. srcFileName, ZSTD_getErrorName(readSizeHint));
  2043. FIO_zstdErrorHelp(prefs, ress, readSizeHint, srcFileName);
  2044. AIO_WritePool_releaseIoJob(writeJob);
  2045. return FIO_ERROR_FRAME_DECODING;
  2046. }
  2047. /* Write block */
  2048. writeJob->usedBufferSize = outBuff.pos;
  2049. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  2050. frameSize += outBuff.pos;
  2051. if (fCtx->nbFilesTotal > 1) {
  2052. size_t srcFileNameSize = strlen(srcFileName);
  2053. if (srcFileNameSize > 18) {
  2054. const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
  2055. DISPLAYUPDATE_PROGRESS(
  2056. "\rDecompress: %2u/%2u files. Current: ...%s : %.*f%s... ",
  2057. fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
  2058. } else {
  2059. DISPLAYUPDATE_PROGRESS("\rDecompress: %2u/%2u files. Current: %s : %.*f%s... ",
  2060. fCtx->currFileIdx+1, fCtx->nbFilesTotal, srcFileName, hrs.precision, hrs.value, hrs.suffix);
  2061. }
  2062. } else {
  2063. DISPLAYUPDATE_PROGRESS("\r%-20.20s : %.*f%s... ",
  2064. srcFileName, hrs.precision, hrs.value, hrs.suffix);
  2065. }
  2066. AIO_ReadPool_consumeBytes(ress->readCtx, inBuff.pos);
  2067. if (readSizeHint == 0) break; /* end of frame */
  2068. /* Fill input buffer */
  2069. { size_t const toDecode = MIN(readSizeHint, ZSTD_DStreamInSize()); /* support large skippable frames */
  2070. if (ress->readCtx->srcBufferLoaded < toDecode) {
  2071. size_t const readSize = AIO_ReadPool_fillBuffer(ress->readCtx, toDecode);
  2072. if (readSize==0) {
  2073. DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
  2074. srcFileName);
  2075. AIO_WritePool_releaseIoJob(writeJob);
  2076. return FIO_ERROR_FRAME_DECODING;
  2077. }
  2078. } } }
  2079. AIO_WritePool_releaseIoJob(writeJob);
  2080. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  2081. return frameSize;
  2082. }
  2083. #ifdef ZSTD_GZDECOMPRESS
  2084. static unsigned long long
  2085. FIO_decompressGzFrame(dRess_t* ress, const char* srcFileName)
  2086. {
  2087. unsigned long long outFileSize = 0;
  2088. z_stream strm;
  2089. int flush = Z_NO_FLUSH;
  2090. int decodingError = 0;
  2091. IOJob_t *writeJob = NULL;
  2092. strm.zalloc = Z_NULL;
  2093. strm.zfree = Z_NULL;
  2094. strm.opaque = Z_NULL;
  2095. strm.next_in = 0;
  2096. strm.avail_in = 0;
  2097. /* see https://www.zlib.net/manual.html */
  2098. if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
  2099. return FIO_ERROR_FRAME_DECODING;
  2100. writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  2101. strm.next_out = (Bytef*)writeJob->buffer;
  2102. strm.avail_out = (uInt)writeJob->bufferSize;
  2103. strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
  2104. strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
  2105. for ( ; ; ) {
  2106. int ret;
  2107. if (strm.avail_in == 0) {
  2108. AIO_ReadPool_consumeAndRefill(ress->readCtx);
  2109. if (ress->readCtx->srcBufferLoaded == 0) flush = Z_FINISH;
  2110. strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
  2111. strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
  2112. }
  2113. ret = inflate(&strm, flush);
  2114. if (ret == Z_BUF_ERROR) {
  2115. DISPLAYLEVEL(1, "zstd: %s: premature gz end \n", srcFileName);
  2116. decodingError = 1; break;
  2117. }
  2118. if (ret != Z_OK && ret != Z_STREAM_END) {
  2119. DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
  2120. decodingError = 1; break;
  2121. }
  2122. { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
  2123. if (decompBytes) {
  2124. writeJob->usedBufferSize = decompBytes;
  2125. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  2126. outFileSize += decompBytes;
  2127. strm.next_out = (Bytef*)writeJob->buffer;
  2128. strm.avail_out = (uInt)writeJob->bufferSize;
  2129. }
  2130. }
  2131. if (ret == Z_STREAM_END) break;
  2132. }
  2133. AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
  2134. if ( (inflateEnd(&strm) != Z_OK) /* release resources ; error detected */
  2135. && (decodingError==0) ) {
  2136. DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
  2137. decodingError = 1;
  2138. }
  2139. AIO_WritePool_releaseIoJob(writeJob);
  2140. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  2141. return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
  2142. }
  2143. #endif
  2144. #ifdef ZSTD_LZMADECOMPRESS
  2145. static unsigned long long
  2146. FIO_decompressLzmaFrame(dRess_t* ress,
  2147. const char* srcFileName, int plain_lzma)
  2148. {
  2149. unsigned long long outFileSize = 0;
  2150. lzma_stream strm = LZMA_STREAM_INIT;
  2151. lzma_action action = LZMA_RUN;
  2152. lzma_ret initRet;
  2153. int decodingError = 0;
  2154. IOJob_t *writeJob = NULL;
  2155. strm.next_in = 0;
  2156. strm.avail_in = 0;
  2157. if (plain_lzma) {
  2158. initRet = lzma_alone_decoder(&strm, UINT64_MAX); /* LZMA */
  2159. } else {
  2160. initRet = lzma_stream_decoder(&strm, UINT64_MAX, 0); /* XZ */
  2161. }
  2162. if (initRet != LZMA_OK) {
  2163. DISPLAYLEVEL(1, "zstd: %s: %s error %d \n",
  2164. plain_lzma ? "lzma_alone_decoder" : "lzma_stream_decoder",
  2165. srcFileName, initRet);
  2166. return FIO_ERROR_FRAME_DECODING;
  2167. }
  2168. writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  2169. strm.next_out = (BYTE*)writeJob->buffer;
  2170. strm.avail_out = writeJob->bufferSize;
  2171. strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
  2172. strm.avail_in = ress->readCtx->srcBufferLoaded;
  2173. for ( ; ; ) {
  2174. lzma_ret ret;
  2175. if (strm.avail_in == 0) {
  2176. AIO_ReadPool_consumeAndRefill(ress->readCtx);
  2177. if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
  2178. strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
  2179. strm.avail_in = ress->readCtx->srcBufferLoaded;
  2180. }
  2181. ret = lzma_code(&strm, action);
  2182. if (ret == LZMA_BUF_ERROR) {
  2183. DISPLAYLEVEL(1, "zstd: %s: premature lzma end \n", srcFileName);
  2184. decodingError = 1; break;
  2185. }
  2186. if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
  2187. DISPLAYLEVEL(1, "zstd: %s: lzma_code decoding error %d \n",
  2188. srcFileName, ret);
  2189. decodingError = 1; break;
  2190. }
  2191. { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
  2192. if (decompBytes) {
  2193. writeJob->usedBufferSize = decompBytes;
  2194. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  2195. outFileSize += decompBytes;
  2196. strm.next_out = (BYTE*)writeJob->buffer;
  2197. strm.avail_out = writeJob->bufferSize;
  2198. } }
  2199. if (ret == LZMA_STREAM_END) break;
  2200. }
  2201. AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
  2202. lzma_end(&strm);
  2203. AIO_WritePool_releaseIoJob(writeJob);
  2204. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  2205. return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
  2206. }
  2207. #endif
  2208. #ifdef ZSTD_LZ4DECOMPRESS
  2209. static unsigned long long
  2210. FIO_decompressLz4Frame(dRess_t* ress, const char* srcFileName)
  2211. {
  2212. unsigned long long filesize = 0;
  2213. LZ4F_errorCode_t nextToLoad = 4;
  2214. LZ4F_decompressionContext_t dCtx;
  2215. LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
  2216. int decodingError = 0;
  2217. IOJob_t *writeJob = NULL;
  2218. if (LZ4F_isError(errorCode)) {
  2219. DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
  2220. return FIO_ERROR_FRAME_DECODING;
  2221. }
  2222. writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
  2223. /* Main Loop */
  2224. for (;nextToLoad;) {
  2225. size_t pos = 0;
  2226. size_t decodedBytes = writeJob->bufferSize;
  2227. int fullBufferDecoded = 0;
  2228. /* Read input */
  2229. AIO_ReadPool_fillBuffer(ress->readCtx, nextToLoad);
  2230. if(!ress->readCtx->srcBufferLoaded) break; /* reached end of file */
  2231. while ((pos < ress->readCtx->srcBufferLoaded) || fullBufferDecoded) { /* still to read, or still to flush */
  2232. /* Decode Input (at least partially) */
  2233. size_t remaining = ress->readCtx->srcBufferLoaded - pos;
  2234. decodedBytes = writeJob->bufferSize;
  2235. nextToLoad = LZ4F_decompress(dCtx, writeJob->buffer, &decodedBytes, (char*)(ress->readCtx->srcBuffer)+pos,
  2236. &remaining, NULL);
  2237. if (LZ4F_isError(nextToLoad)) {
  2238. DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
  2239. srcFileName, LZ4F_getErrorName(nextToLoad));
  2240. decodingError = 1; nextToLoad = 0; break;
  2241. }
  2242. pos += remaining;
  2243. assert(pos <= ress->readCtx->srcBufferLoaded);
  2244. fullBufferDecoded = decodedBytes == writeJob->bufferSize;
  2245. /* Write Block */
  2246. if (decodedBytes) {
  2247. UTIL_HumanReadableSize_t hrs;
  2248. writeJob->usedBufferSize = decodedBytes;
  2249. AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
  2250. filesize += decodedBytes;
  2251. hrs = UTIL_makeHumanReadableSize(filesize);
  2252. DISPLAYUPDATE_PROGRESS("\rDecompressed : %.*f%s ", hrs.precision, hrs.value, hrs.suffix);
  2253. }
  2254. if (!nextToLoad) break;
  2255. }
  2256. AIO_ReadPool_consumeBytes(ress->readCtx, pos);
  2257. }
  2258. if (nextToLoad!=0) {
  2259. DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
  2260. decodingError=1;
  2261. }
  2262. LZ4F_freeDecompressionContext(dCtx);
  2263. AIO_WritePool_releaseIoJob(writeJob);
  2264. AIO_WritePool_sparseWriteEnd(ress->writeCtx);
  2265. return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
  2266. }
  2267. #endif
  2268. /** FIO_decompressFrames() :
  2269. * Find and decode frames inside srcFile
  2270. * srcFile presumed opened and valid
  2271. * @return : 0 : OK
  2272. * 1 : error
  2273. */
  2274. static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
  2275. dRess_t ress, const FIO_prefs_t* const prefs,
  2276. const char* dstFileName, const char* srcFileName)
  2277. {
  2278. unsigned readSomething = 0;
  2279. unsigned long long filesize = 0;
  2280. int passThrough = prefs->passThrough;
  2281. if (passThrough == -1) {
  2282. /* If pass-through mode is not explicitly enabled or disabled,
  2283. * default to the legacy behavior of enabling it if we are writing
  2284. * to stdout with the overwrite flag enabled.
  2285. */
  2286. passThrough = prefs->overwrite && !strcmp(dstFileName, stdoutmark);
  2287. }
  2288. assert(passThrough == 0 || passThrough == 1);
  2289. /* for each frame */
  2290. for ( ; ; ) {
  2291. /* check magic number -> version */
  2292. size_t const toRead = 4;
  2293. const BYTE* buf;
  2294. AIO_ReadPool_fillBuffer(ress.readCtx, toRead);
  2295. buf = (const BYTE*)ress.readCtx->srcBuffer;
  2296. if (ress.readCtx->srcBufferLoaded==0) {
  2297. if (readSomething==0) { /* srcFile is empty (which is invalid) */
  2298. DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
  2299. return 1;
  2300. } /* else, just reached frame boundary */
  2301. break; /* no more input */
  2302. }
  2303. readSomething = 1; /* there is at least 1 byte in srcFile */
  2304. if (ress.readCtx->srcBufferLoaded < toRead) { /* not enough input to check magic number */
  2305. if (passThrough) {
  2306. return FIO_passThrough(&ress);
  2307. }
  2308. DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
  2309. return 1;
  2310. }
  2311. if (ZSTD_isFrame(buf, ress.readCtx->srcBufferLoaded)) {
  2312. unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, prefs, srcFileName, filesize);
  2313. if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
  2314. filesize += frameSize;
  2315. } else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
  2316. #ifdef ZSTD_GZDECOMPRESS
  2317. unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFileName);
  2318. if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
  2319. filesize += frameSize;
  2320. #else
  2321. DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
  2322. return 1;
  2323. #endif
  2324. } else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */
  2325. || (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
  2326. #ifdef ZSTD_LZMADECOMPRESS
  2327. unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFileName, buf[0] != 0xFD);
  2328. if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
  2329. filesize += frameSize;
  2330. #else
  2331. DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
  2332. return 1;
  2333. #endif
  2334. } else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
  2335. #ifdef ZSTD_LZ4DECOMPRESS
  2336. unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFileName);
  2337. if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
  2338. filesize += frameSize;
  2339. #else
  2340. DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
  2341. return 1;
  2342. #endif
  2343. } else if (passThrough) {
  2344. return FIO_passThrough(&ress);
  2345. } else {
  2346. DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
  2347. return 1;
  2348. } } /* for each frame */
  2349. /* Final Status */
  2350. fCtx->totalBytesOutput += (size_t)filesize;
  2351. DISPLAY_PROGRESS("\r%79s\r", "");
  2352. if (FIO_shouldDisplayFileSummary(fCtx))
  2353. DISPLAY_SUMMARY("%-20s: %llu bytes \n", srcFileName, filesize);
  2354. return 0;
  2355. }
  2356. /** FIO_decompressDstFile() :
  2357. open `dstFileName`, or pass-through if writeCtx's file is already != 0,
  2358. then start decompression process (FIO_decompressFrames()).
  2359. @return : 0 : OK
  2360. 1 : operation aborted
  2361. */
  2362. static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
  2363. FIO_prefs_t* const prefs,
  2364. dRess_t ress,
  2365. const char* dstFileName,
  2366. const char* srcFileName,
  2367. const stat_t* srcFileStat)
  2368. {
  2369. int result;
  2370. int releaseDstFile = 0;
  2371. int transferStat = 0;
  2372. int dstFd = 0;
  2373. if ((AIO_WritePool_getFile(ress.writeCtx) == NULL) && (prefs->testMode == 0)) {
  2374. FILE *dstFile;
  2375. int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
  2376. if ( strcmp(srcFileName, stdinmark) /* special case : don't transfer permissions from stdin */
  2377. && strcmp(dstFileName, stdoutmark)
  2378. && UTIL_isRegularFileStat(srcFileStat) ) {
  2379. transferStat = 1;
  2380. dstFilePermissions = TEMPORARY_FILE_PERMISSIONS;
  2381. }
  2382. releaseDstFile = 1;
  2383. dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
  2384. if (dstFile==NULL) return 1;
  2385. dstFd = fileno(dstFile);
  2386. AIO_WritePool_setFile(ress.writeCtx, dstFile);
  2387. /* Must only be added after FIO_openDstFile() succeeds.
  2388. * Otherwise we may delete the destination file if it already exists,
  2389. * and the user presses Ctrl-C when asked if they wish to overwrite.
  2390. */
  2391. addHandler(dstFileName);
  2392. }
  2393. result = FIO_decompressFrames(fCtx, ress, prefs, dstFileName, srcFileName);
  2394. if (releaseDstFile) {
  2395. clearHandler();
  2396. if (transferStat) {
  2397. UTIL_setFDStat(dstFd, dstFileName, srcFileStat);
  2398. }
  2399. if (AIO_WritePool_closeFile(ress.writeCtx)) {
  2400. DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
  2401. result = 1;
  2402. }
  2403. if (transferStat) {
  2404. UTIL_utime(dstFileName, srcFileStat);
  2405. }
  2406. if ( (result != 0) /* operation failure */
  2407. && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */
  2408. ) {
  2409. FIO_removeFile(dstFileName); /* remove decompression artefact; note: don't do anything special if remove() fails */
  2410. }
  2411. }
  2412. return result;
  2413. }
  2414. /** FIO_decompressSrcFile() :
  2415. Open `srcFileName`, transfer control to decompressDstFile()
  2416. @return : 0 : OK
  2417. 1 : error
  2418. */
  2419. static int FIO_decompressSrcFile(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, dRess_t ress, const char* dstFileName, const char* srcFileName)
  2420. {
  2421. FILE* srcFile;
  2422. stat_t srcFileStat;
  2423. int result;
  2424. U64 fileSize = UTIL_FILESIZE_UNKNOWN;
  2425. if (UTIL_isDirectory(srcFileName)) {
  2426. DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
  2427. return 1;
  2428. }
  2429. srcFile = FIO_openSrcFile(prefs, srcFileName, &srcFileStat);
  2430. if (srcFile==NULL) return 1;
  2431. /* Don't use AsyncIO for small files */
  2432. if (strcmp(srcFileName, stdinmark)) /* Stdin doesn't have stats */
  2433. fileSize = UTIL_getFileSizeStat(&srcFileStat);
  2434. if(fileSize != UTIL_FILESIZE_UNKNOWN && fileSize < ZSTD_BLOCKSIZE_MAX * 3) {
  2435. AIO_ReadPool_setAsync(ress.readCtx, 0);
  2436. AIO_WritePool_setAsync(ress.writeCtx, 0);
  2437. } else {
  2438. AIO_ReadPool_setAsync(ress.readCtx, 1);
  2439. AIO_WritePool_setAsync(ress.writeCtx, 1);
  2440. }
  2441. AIO_ReadPool_setFile(ress.readCtx, srcFile);
  2442. result = FIO_decompressDstFile(fCtx, prefs, ress, dstFileName, srcFileName, &srcFileStat);
  2443. AIO_ReadPool_setFile(ress.readCtx, NULL);
  2444. /* Close file */
  2445. if (fclose(srcFile)) {
  2446. DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); /* error should not happen */
  2447. return 1;
  2448. }
  2449. if ( prefs->removeSrcFile /* --rm */
  2450. && (result==0) /* decompression successful */
  2451. && strcmp(srcFileName, stdinmark) ) /* not stdin */ {
  2452. /* We must clear the handler, since after this point calling it would
  2453. * delete both the source and destination files.
  2454. */
  2455. clearHandler();
  2456. if (FIO_removeFile(srcFileName)) {
  2457. /* failed to remove src file */
  2458. DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
  2459. return 1;
  2460. } }
  2461. return result;
  2462. }
  2463. int FIO_decompressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs,
  2464. const char* dstFileName, const char* srcFileName,
  2465. const char* dictFileName)
  2466. {
  2467. dRess_t const ress = FIO_createDResources(prefs, dictFileName);
  2468. int const decodingError = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
  2469. FIO_freeDResources(ress);
  2470. return decodingError;
  2471. }
  2472. static const char *suffixList[] = {
  2473. ZSTD_EXTENSION,
  2474. TZSTD_EXTENSION,
  2475. #ifndef ZSTD_NODECOMPRESS
  2476. ZSTD_ALT_EXTENSION,
  2477. #endif
  2478. #ifdef ZSTD_GZDECOMPRESS
  2479. GZ_EXTENSION,
  2480. TGZ_EXTENSION,
  2481. #endif
  2482. #ifdef ZSTD_LZMADECOMPRESS
  2483. LZMA_EXTENSION,
  2484. XZ_EXTENSION,
  2485. TXZ_EXTENSION,
  2486. #endif
  2487. #ifdef ZSTD_LZ4DECOMPRESS
  2488. LZ4_EXTENSION,
  2489. TLZ4_EXTENSION,
  2490. #endif
  2491. NULL
  2492. };
  2493. static const char *suffixListStr =
  2494. ZSTD_EXTENSION "/" TZSTD_EXTENSION
  2495. #ifdef ZSTD_GZDECOMPRESS
  2496. "/" GZ_EXTENSION "/" TGZ_EXTENSION
  2497. #endif
  2498. #ifdef ZSTD_LZMADECOMPRESS
  2499. "/" LZMA_EXTENSION "/" XZ_EXTENSION "/" TXZ_EXTENSION
  2500. #endif
  2501. #ifdef ZSTD_LZ4DECOMPRESS
  2502. "/" LZ4_EXTENSION "/" TLZ4_EXTENSION
  2503. #endif
  2504. ;
  2505. /* FIO_determineDstName() :
  2506. * create a destination filename from a srcFileName.
  2507. * @return a pointer to it.
  2508. * @return == NULL if there is an error */
  2509. static const char*
  2510. FIO_determineDstName(const char* srcFileName, const char* outDirName)
  2511. {
  2512. static size_t dfnbCapacity = 0;
  2513. static char* dstFileNameBuffer = NULL; /* using static allocation : this function cannot be multi-threaded */
  2514. size_t dstFileNameEndPos;
  2515. char* outDirFilename = NULL;
  2516. const char* dstSuffix = "";
  2517. size_t dstSuffixLen = 0;
  2518. size_t sfnSize = strlen(srcFileName);
  2519. size_t srcSuffixLen;
  2520. const char* const srcSuffix = strrchr(srcFileName, '.');
  2521. if(!strcmp(srcFileName, stdinmark)) {
  2522. return stdoutmark;
  2523. }
  2524. if (srcSuffix == NULL) {
  2525. DISPLAYLEVEL(1,
  2526. "zstd: %s: unknown suffix (%s expected). "
  2527. "Can't derive the output file name. "
  2528. "Specify it with -o dstFileName. Ignoring.\n",
  2529. srcFileName, suffixListStr);
  2530. return NULL;
  2531. }
  2532. srcSuffixLen = strlen(srcSuffix);
  2533. {
  2534. const char** matchedSuffixPtr;
  2535. for (matchedSuffixPtr = suffixList; *matchedSuffixPtr != NULL; matchedSuffixPtr++) {
  2536. if (!strcmp(*matchedSuffixPtr, srcSuffix)) {
  2537. break;
  2538. }
  2539. }
  2540. /* check suffix is authorized */
  2541. if (sfnSize <= srcSuffixLen || *matchedSuffixPtr == NULL) {
  2542. DISPLAYLEVEL(1,
  2543. "zstd: %s: unknown suffix (%s expected). "
  2544. "Can't derive the output file name. "
  2545. "Specify it with -o dstFileName. Ignoring.\n",
  2546. srcFileName, suffixListStr);
  2547. return NULL;
  2548. }
  2549. if ((*matchedSuffixPtr)[1] == 't') {
  2550. dstSuffix = ".tar";
  2551. dstSuffixLen = strlen(dstSuffix);
  2552. }
  2553. }
  2554. if (outDirName) {
  2555. outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, 0);
  2556. sfnSize = strlen(outDirFilename);
  2557. assert(outDirFilename != NULL);
  2558. }
  2559. if (dfnbCapacity+srcSuffixLen <= sfnSize+1+dstSuffixLen) {
  2560. /* allocate enough space to write dstFilename into it */
  2561. free(dstFileNameBuffer);
  2562. dfnbCapacity = sfnSize + 20;
  2563. dstFileNameBuffer = (char*)malloc(dfnbCapacity);
  2564. if (dstFileNameBuffer==NULL)
  2565. EXM_THROW(74, "%s : not enough memory for dstFileName",
  2566. strerror(errno));
  2567. }
  2568. /* return dst name == src name truncated from suffix */
  2569. assert(dstFileNameBuffer != NULL);
  2570. dstFileNameEndPos = sfnSize - srcSuffixLen;
  2571. if (outDirFilename) {
  2572. memcpy(dstFileNameBuffer, outDirFilename, dstFileNameEndPos);
  2573. free(outDirFilename);
  2574. } else {
  2575. memcpy(dstFileNameBuffer, srcFileName, dstFileNameEndPos);
  2576. }
  2577. /* The short tar extensions tzst, tgz, txz and tlz4 files should have "tar"
  2578. * extension on decompression. Also writes terminating null. */
  2579. strcpy(dstFileNameBuffer + dstFileNameEndPos, dstSuffix);
  2580. return dstFileNameBuffer;
  2581. /* note : dstFileNameBuffer memory is not going to be free */
  2582. }
  2583. int
  2584. FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
  2585. FIO_prefs_t* const prefs,
  2586. const char** srcNamesTable,
  2587. const char* outMirroredRootDirName,
  2588. const char* outDirName, const char* outFileName,
  2589. const char* dictFileName)
  2590. {
  2591. int status;
  2592. int error = 0;
  2593. dRess_t ress = FIO_createDResources(prefs, dictFileName);
  2594. if (outFileName) {
  2595. if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
  2596. FIO_freeDResources(ress);
  2597. return 1;
  2598. }
  2599. if (!prefs->testMode) {
  2600. FILE* dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
  2601. if (dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
  2602. AIO_WritePool_setFile(ress.writeCtx, dstFile);
  2603. }
  2604. for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
  2605. status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
  2606. if (!status) fCtx->nbFilesProcessed++;
  2607. error |= status;
  2608. }
  2609. if ((!prefs->testMode) && (AIO_WritePool_closeFile(ress.writeCtx)))
  2610. EXM_THROW(72, "Write error : %s : cannot properly close output file",
  2611. strerror(errno));
  2612. } else {
  2613. if (outMirroredRootDirName)
  2614. UTIL_mirrorSourceFilesDirectories(srcNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
  2615. for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) { /* create dstFileName */
  2616. const char* const srcFileName = srcNamesTable[fCtx->currFileIdx];
  2617. const char* dstFileName = NULL;
  2618. if (outMirroredRootDirName) {
  2619. char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
  2620. if (validMirroredDirName) {
  2621. dstFileName = FIO_determineDstName(srcFileName, validMirroredDirName);
  2622. free(validMirroredDirName);
  2623. } else {
  2624. DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot decompress '%s' into '%s'\n", srcFileName, outMirroredRootDirName);
  2625. }
  2626. } else {
  2627. dstFileName = FIO_determineDstName(srcFileName, outDirName);
  2628. }
  2629. if (dstFileName == NULL) { error=1; continue; }
  2630. status = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
  2631. if (!status) fCtx->nbFilesProcessed++;
  2632. error |= status;
  2633. }
  2634. if (outDirName)
  2635. FIO_checkFilenameCollisions(srcNamesTable , (unsigned)fCtx->nbFilesTotal);
  2636. }
  2637. if (FIO_shouldDisplayMultipleFileSummary(fCtx)) {
  2638. DISPLAY_PROGRESS("\r%79s\r", "");
  2639. DISPLAY_SUMMARY("%d files decompressed : %6llu bytes total \n",
  2640. fCtx->nbFilesProcessed, (unsigned long long)fCtx->totalBytesOutput);
  2641. }
  2642. FIO_freeDResources(ress);
  2643. return error;
  2644. }
  2645. /* **************************************************************************
  2646. * .zst file info (--list command)
  2647. ***************************************************************************/
  2648. typedef struct {
  2649. U64 decompressedSize;
  2650. U64 compressedSize;
  2651. U64 windowSize;
  2652. int numActualFrames;
  2653. int numSkippableFrames;
  2654. int decompUnavailable;
  2655. int usesCheck;
  2656. BYTE checksum[4];
  2657. U32 nbFiles;
  2658. unsigned dictID;
  2659. } fileInfo_t;
  2660. typedef enum {
  2661. info_success=0,
  2662. info_frame_error=1,
  2663. info_not_zstd=2,
  2664. info_file_error=3,
  2665. info_truncated_input=4
  2666. } InfoError;
  2667. #define ERROR_IF(c,n,...) { \
  2668. if (c) { \
  2669. DISPLAYLEVEL(1, __VA_ARGS__); \
  2670. DISPLAYLEVEL(1, " \n"); \
  2671. return n; \
  2672. } \
  2673. }
  2674. static InfoError
  2675. FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
  2676. {
  2677. /* begin analyzing frame */
  2678. for ( ; ; ) {
  2679. BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
  2680. size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
  2681. if (numBytesRead < ZSTD_FRAMEHEADERSIZE_MIN(ZSTD_f_zstd1)) {
  2682. if ( feof(srcFile)
  2683. && (numBytesRead == 0)
  2684. && (info->compressedSize > 0)
  2685. && (info->compressedSize != UTIL_FILESIZE_UNKNOWN) ) {
  2686. unsigned long long file_position = (unsigned long long) LONG_TELL(srcFile);
  2687. unsigned long long file_size = (unsigned long long) info->compressedSize;
  2688. ERROR_IF(file_position != file_size, info_truncated_input,
  2689. "Error: seeked to position %llu, which is beyond file size of %llu\n",
  2690. file_position,
  2691. file_size);
  2692. break; /* correct end of file => success */
  2693. }
  2694. ERROR_IF(feof(srcFile), info_not_zstd, "Error: reached end of file with incomplete frame");
  2695. ERROR_IF(1, info_frame_error, "Error: did not reach end of file but ran out of frames");
  2696. }
  2697. { U32 const magicNumber = MEM_readLE32(headerBuffer);
  2698. /* Zstandard frame */
  2699. if (magicNumber == ZSTD_MAGICNUMBER) {
  2700. ZSTD_frameHeader header;
  2701. U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
  2702. if ( frameContentSize == ZSTD_CONTENTSIZE_ERROR
  2703. || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN ) {
  2704. info->decompUnavailable = 1;
  2705. } else {
  2706. info->decompressedSize += frameContentSize;
  2707. }
  2708. ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
  2709. info_frame_error, "Error: could not decode frame header");
  2710. if (info->dictID != 0 && info->dictID != header.dictID) {
  2711. DISPLAY("WARNING: File contains multiple frames with different dictionary IDs. Showing dictID 0 instead");
  2712. info->dictID = 0;
  2713. } else {
  2714. info->dictID = header.dictID;
  2715. }
  2716. info->windowSize = header.windowSize;
  2717. /* move to the end of the frame header */
  2718. { size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
  2719. ERROR_IF(ZSTD_isError(headerSize), info_frame_error, "Error: could not determine frame header size");
  2720. ERROR_IF(fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR) != 0,
  2721. info_frame_error, "Error: could not move to end of frame header");
  2722. }
  2723. /* skip all blocks in the frame */
  2724. { int lastBlock = 0;
  2725. do {
  2726. BYTE blockHeaderBuffer[3];
  2727. ERROR_IF(fread(blockHeaderBuffer, 1, 3, srcFile) != 3,
  2728. info_frame_error, "Error while reading block header");
  2729. { U32 const blockHeader = MEM_readLE24(blockHeaderBuffer);
  2730. U32 const blockTypeID = (blockHeader >> 1) & 3;
  2731. U32 const isRLE = (blockTypeID == 1);
  2732. U32 const isWrongBlock = (blockTypeID == 3);
  2733. long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3);
  2734. ERROR_IF(isWrongBlock, info_frame_error, "Error: unsupported block type");
  2735. lastBlock = blockHeader & 1;
  2736. ERROR_IF(fseek(srcFile, blockSize, SEEK_CUR) != 0,
  2737. info_frame_error, "Error: could not skip to end of block");
  2738. }
  2739. } while (lastBlock != 1);
  2740. }
  2741. /* check if checksum is used */
  2742. { BYTE const frameHeaderDescriptor = headerBuffer[4];
  2743. int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
  2744. if (contentChecksumFlag) {
  2745. info->usesCheck = 1;
  2746. ERROR_IF(fread(info->checksum, 1, 4, srcFile) != 4,
  2747. info_frame_error, "Error: could not read checksum");
  2748. } }
  2749. info->numActualFrames++;
  2750. }
  2751. /* Skippable frame */
  2752. else if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
  2753. U32 const frameSize = MEM_readLE32(headerBuffer + 4);
  2754. long const seek = (long)(8 + frameSize - numBytesRead);
  2755. ERROR_IF(LONG_SEEK(srcFile, seek, SEEK_CUR) != 0,
  2756. info_frame_error, "Error: could not find end of skippable frame");
  2757. info->numSkippableFrames++;
  2758. }
  2759. /* unknown content */
  2760. else {
  2761. return info_not_zstd;
  2762. }
  2763. } /* magic number analysis */
  2764. } /* end analyzing frames */
  2765. return info_success;
  2766. }
  2767. static InfoError
  2768. getFileInfo_fileConfirmed(fileInfo_t* info, const char* inFileName)
  2769. {
  2770. InfoError status;
  2771. stat_t srcFileStat;
  2772. FILE* const srcFile = FIO_openSrcFile(NULL, inFileName, &srcFileStat);
  2773. ERROR_IF(srcFile == NULL, info_file_error, "Error: could not open source file %s", inFileName);
  2774. info->compressedSize = UTIL_getFileSizeStat(&srcFileStat);
  2775. status = FIO_analyzeFrames(info, srcFile);
  2776. fclose(srcFile);
  2777. info->nbFiles = 1;
  2778. return status;
  2779. }
  2780. /** getFileInfo() :
  2781. * Reads information from file, stores in *info
  2782. * @return : InfoError status
  2783. */
  2784. static InfoError
  2785. getFileInfo(fileInfo_t* info, const char* srcFileName)
  2786. {
  2787. ERROR_IF(!UTIL_isRegularFile(srcFileName),
  2788. info_file_error, "Error : %s is not a file", srcFileName);
  2789. return getFileInfo_fileConfirmed(info, srcFileName);
  2790. }
  2791. static void
  2792. displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
  2793. {
  2794. UTIL_HumanReadableSize_t const window_hrs = UTIL_makeHumanReadableSize(info->windowSize);
  2795. UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(info->compressedSize);
  2796. UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(info->decompressedSize);
  2797. double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/(double)info->compressedSize;
  2798. const char* const checkString = (info->usesCheck ? "XXH64" : "None");
  2799. if (displayLevel <= 2) {
  2800. if (!info->decompUnavailable) {
  2801. DISPLAYOUT("%6d %5d %6.*f%4s %8.*f%4s %5.3f %5s %s\n",
  2802. info->numSkippableFrames + info->numActualFrames,
  2803. info->numSkippableFrames,
  2804. compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
  2805. decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
  2806. ratio, checkString, inFileName);
  2807. } else {
  2808. DISPLAYOUT("%6d %5d %6.*f%4s %5s %s\n",
  2809. info->numSkippableFrames + info->numActualFrames,
  2810. info->numSkippableFrames,
  2811. compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
  2812. checkString, inFileName);
  2813. }
  2814. } else {
  2815. DISPLAYOUT("%s \n", inFileName);
  2816. DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
  2817. if (info->numSkippableFrames)
  2818. DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
  2819. DISPLAYOUT("DictID: %u\n", info->dictID);
  2820. DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
  2821. window_hrs.precision, window_hrs.value, window_hrs.suffix,
  2822. (unsigned long long)info->windowSize);
  2823. DISPLAYOUT("Compressed Size: %.*f%s (%llu B)\n",
  2824. compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
  2825. (unsigned long long)info->compressedSize);
  2826. if (!info->decompUnavailable) {
  2827. DISPLAYOUT("Decompressed Size: %.*f%s (%llu B)\n",
  2828. decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
  2829. (unsigned long long)info->decompressedSize);
  2830. DISPLAYOUT("Ratio: %.4f\n", ratio);
  2831. }
  2832. if (info->usesCheck && info->numActualFrames == 1) {
  2833. DISPLAYOUT("Check: %s %02x%02x%02x%02x\n", checkString,
  2834. info->checksum[3], info->checksum[2],
  2835. info->checksum[1], info->checksum[0]
  2836. );
  2837. } else {
  2838. DISPLAYOUT("Check: %s\n", checkString);
  2839. }
  2840. DISPLAYOUT("\n");
  2841. }
  2842. }
  2843. static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2)
  2844. {
  2845. fileInfo_t total;
  2846. memset(&total, 0, sizeof(total));
  2847. total.numActualFrames = fi1.numActualFrames + fi2.numActualFrames;
  2848. total.numSkippableFrames = fi1.numSkippableFrames + fi2.numSkippableFrames;
  2849. total.compressedSize = fi1.compressedSize + fi2.compressedSize;
  2850. total.decompressedSize = fi1.decompressedSize + fi2.decompressedSize;
  2851. total.decompUnavailable = fi1.decompUnavailable | fi2.decompUnavailable;
  2852. total.usesCheck = fi1.usesCheck & fi2.usesCheck;
  2853. total.nbFiles = fi1.nbFiles + fi2.nbFiles;
  2854. return total;
  2855. }
  2856. static int
  2857. FIO_listFile(fileInfo_t* total, const char* inFileName, int displayLevel)
  2858. {
  2859. fileInfo_t info;
  2860. memset(&info, 0, sizeof(info));
  2861. { InfoError const error = getFileInfo(&info, inFileName);
  2862. switch (error) {
  2863. case info_frame_error:
  2864. /* display error, but provide output */
  2865. DISPLAYLEVEL(1, "Error while parsing \"%s\" \n", inFileName);
  2866. break;
  2867. case info_not_zstd:
  2868. DISPLAYOUT("File \"%s\" not compressed by zstd \n", inFileName);
  2869. if (displayLevel > 2) DISPLAYOUT("\n");
  2870. return 1;
  2871. case info_file_error:
  2872. /* error occurred while opening the file */
  2873. if (displayLevel > 2) DISPLAYOUT("\n");
  2874. return 1;
  2875. case info_truncated_input:
  2876. DISPLAYOUT("File \"%s\" is truncated \n", inFileName);
  2877. if (displayLevel > 2) DISPLAYOUT("\n");
  2878. return 1;
  2879. case info_success:
  2880. default:
  2881. break;
  2882. }
  2883. displayInfo(inFileName, &info, displayLevel);
  2884. *total = FIO_addFInfo(*total, info);
  2885. assert(error == info_success || error == info_frame_error);
  2886. return (int)error;
  2887. }
  2888. }
  2889. int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel)
  2890. {
  2891. /* ensure no specified input is stdin (needs fseek() capability) */
  2892. { unsigned u;
  2893. for (u=0; u<numFiles;u++) {
  2894. ERROR_IF(!strcmp (filenameTable[u], stdinmark),
  2895. 1, "zstd: --list does not support reading from standard input");
  2896. } }
  2897. if (numFiles == 0) {
  2898. if (!UTIL_isConsole(stdin)) {
  2899. DISPLAYLEVEL(1, "zstd: --list does not support reading from standard input \n");
  2900. }
  2901. DISPLAYLEVEL(1, "No files given \n");
  2902. return 1;
  2903. }
  2904. if (displayLevel <= 2) {
  2905. DISPLAYOUT("Frames Skips Compressed Uncompressed Ratio Check Filename\n");
  2906. }
  2907. { int error = 0;
  2908. fileInfo_t total;
  2909. memset(&total, 0, sizeof(total));
  2910. total.usesCheck = 1;
  2911. /* --list each file, and check for any error */
  2912. { unsigned u;
  2913. for (u=0; u<numFiles;u++) {
  2914. error |= FIO_listFile(&total, filenameTable[u], displayLevel);
  2915. } }
  2916. if (numFiles > 1 && displayLevel <= 2) { /* display total */
  2917. UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(total.compressedSize);
  2918. UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(total.decompressedSize);
  2919. double const ratio = (total.compressedSize == 0) ? 0 : ((double)total.decompressedSize)/(double)total.compressedSize;
  2920. const char* const checkString = (total.usesCheck ? "XXH64" : "");
  2921. DISPLAYOUT("----------------------------------------------------------------- \n");
  2922. if (total.decompUnavailable) {
  2923. DISPLAYOUT("%6d %5d %6.*f%4s %5s %u files\n",
  2924. total.numSkippableFrames + total.numActualFrames,
  2925. total.numSkippableFrames,
  2926. compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
  2927. checkString, (unsigned)total.nbFiles);
  2928. } else {
  2929. DISPLAYOUT("%6d %5d %6.*f%4s %8.*f%4s %5.3f %5s %u files\n",
  2930. total.numSkippableFrames + total.numActualFrames,
  2931. total.numSkippableFrames,
  2932. compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
  2933. decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
  2934. ratio, checkString, (unsigned)total.nbFiles);
  2935. } }
  2936. return error;
  2937. }
  2938. }
  2939. #endif /* #ifndef ZSTD_NODECOMPRESS */