LZMA.pas 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  1. unit LZMA;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2024 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Interface to the LZMA/LZMA2 compression DLL and EXEs in lzma2\Encoder, used by
  8. ISCmplr.
  9. }
  10. interface
  11. {$I VERSION.INC}
  12. uses
  13. Windows, SysUtils,
  14. Compress, Int64Em;
  15. function LZMAInitCompressFunctions(Module: HMODULE): Boolean;
  16. function LZMAGetLevel(const Value: String; var Level: Integer): Boolean;
  17. const
  18. clLZMAFast = 1;
  19. clLZMANormal = 2;
  20. clLZMAMax = 3;
  21. clLZMAUltra = 4;
  22. clLZMAUltra64 = 5;
  23. type
  24. { Internally-used types }
  25. TLZMASRes = type Integer;
  26. TLZMACompressorCustomWorker = class;
  27. TLZMACompressorProps = class(TCompressorProps)
  28. public
  29. Algorithm: Integer;
  30. BlockSize: Integer;
  31. BTMode: Integer;
  32. DictionarySize: Integer;
  33. NumBlockThreads: Integer;
  34. NumFastBytes: Integer;
  35. NumThreads: Integer;
  36. WorkerProcessFilename: String;
  37. constructor Create;
  38. end;
  39. { Internally-used records }
  40. TLZMAEncoderProps = record
  41. Algorithm: Integer;
  42. BlockSize: Integer;
  43. BTMode: Integer;
  44. DictionarySize: Integer;
  45. NumBlockThreads: Integer;
  46. NumFastBytes: Integer;
  47. NumThreads: Integer;
  48. end;
  49. TLZMACompressorRingBuffer = record
  50. Count: Longint; { updated by reader and writer using InterlockedExchangeAdd only }
  51. WriterOffset: Longint; { accessed only by writer thread }
  52. ReaderOffset: Longint; { accessed only by reader thread }
  53. Buf: array[0..$FFFFF] of Byte;
  54. end;
  55. PLZMACompressorSharedEvents = ^TLZMACompressorSharedEvents;
  56. TLZMACompressorSharedEvents = record
  57. TerminateWorkerEvent: THandle;
  58. StartEncodeEvent: THandle;
  59. EndWaitOnInputEvent: THandle;
  60. EndWaitOnOutputEvent: THandle;
  61. EndWaitOnProgressEvent: THandle;
  62. WorkerWaitingOnInputEvent: THandle;
  63. WorkerWaitingOnOutputEvent: THandle;
  64. WorkerHasProgressEvent: THandle;
  65. WorkerEncodeFinishedEvent: THandle;
  66. end;
  67. PLZMACompressorSharedData = ^TLZMACompressorSharedData;
  68. TLZMACompressorSharedData = record
  69. NoMoreInput: BOOL;
  70. ProgressKB: LongWord;
  71. EncodeResult: TLZMASRes;
  72. InputBuffer: TLZMACompressorRingBuffer;
  73. OutputBuffer: TLZMACompressorRingBuffer;
  74. end;
  75. PLZMACompressorProcessData = ^TLZMACompressorProcessData;
  76. TLZMACompressorProcessData = record
  77. StructSize: LongWord;
  78. ParentProcess: THandle;
  79. LZMA2: BOOL;
  80. EncoderProps: TLZMAEncoderProps;
  81. Events: TLZMACompressorSharedEvents;
  82. SharedDataStructSize: LongWord;
  83. SharedDataMapping: THandle;
  84. end;
  85. TLZMACompressor = class(TCustomCompressor)
  86. private
  87. FUseLZMA2: Boolean;
  88. FEvents: TLZMACompressorSharedEvents;
  89. FShared: PLZMACompressorSharedData;
  90. FWorker: TLZMACompressorCustomWorker;
  91. FEncodeStarted: Boolean;
  92. FEncodeFinished: Boolean;
  93. FLastInputWriteCount: LongWord;
  94. FLastProgressKB: LongWord;
  95. procedure FlushOutputBuffer(const OnlyOptimalSize: Boolean);
  96. procedure InitializeProps(const CompressionLevel: Integer;
  97. const ACompressorProps: TCompressorProps);
  98. class function IsEventSet(const AEvent: THandle): Boolean;
  99. class procedure SatisfyWorkerWait(const AWorkerEvent, AMainEvent: THandle);
  100. procedure SatisfyWorkerWaitOnInput;
  101. procedure SatisfyWorkerWaitOnOutput;
  102. procedure StartEncode;
  103. procedure UpdateProgress;
  104. procedure WaitForWorkerEvent;
  105. protected
  106. procedure DoCompress(const Buffer; Count: Longint); override;
  107. procedure DoFinish; override;
  108. public
  109. constructor Create(AWriteProc: TCompressorWriteProc;
  110. AProgressProc: TCompressorProgressProc; CompressionLevel: Integer;
  111. ACompressorProps: TCompressorProps); override;
  112. destructor Destroy; override;
  113. end;
  114. TLZMA2Compressor = class(TLZMACompressor)
  115. public
  116. constructor Create(AWriteProc: TCompressorWriteProc;
  117. AProgressProc: TCompressorProgressProc; CompressionLevel: Integer;
  118. ACompressorProps: TCompressorProps); override;
  119. end;
  120. { Internally-used classes }
  121. TLZMACompressorCustomWorker = class
  122. protected
  123. FEvents: PLZMACompressorSharedEvents;
  124. FShared: PLZMACompressorSharedData;
  125. public
  126. constructor Create(const AEvents: PLZMACompressorSharedEvents); virtual;
  127. function GetExitHandle: THandle; virtual; abstract;
  128. procedure SetProps(const LZMA2: Boolean; const EncProps: TLZMAEncoderProps);
  129. virtual; abstract;
  130. procedure UnexpectedTerminationError; virtual; abstract;
  131. end;
  132. implementation
  133. const
  134. ISLZMA_EXE_VERSION = 101;
  135. type
  136. TLZMACompressorHandle = type Pointer;
  137. TLZMAWorkerThread = class(TLZMACompressorCustomWorker)
  138. private
  139. FThread: THandle;
  140. FLZMAHandle: TLZMACompressorHandle;
  141. FReadLock, FWriteLock, FProgressLock: Integer;
  142. FLastProgressTick: DWORD;
  143. function FillBuffer(const AWrite: Boolean; const Data: Pointer;
  144. Size: Cardinal; var ProcessedSize: Cardinal): HRESULT;
  145. function ProgressMade(const TotalBytesProcessed: Integer64): HRESULT;
  146. function Read(var Data; Size: Cardinal; var ProcessedSize: Cardinal): HRESULT;
  147. function WakeMainAndWaitUntil(const AWakeEvent, AWaitEvent: THandle): HRESULT;
  148. procedure WorkerThreadProc;
  149. function Write(const Data; Size: Cardinal; var ProcessedSize: Cardinal): HRESULT;
  150. public
  151. constructor Create(const AEvents: PLZMACompressorSharedEvents); override;
  152. destructor Destroy; override;
  153. function GetExitHandle: THandle; override;
  154. procedure SetProps(const LZMA2: Boolean; const EncProps: TLZMAEncoderProps);
  155. override;
  156. procedure UnexpectedTerminationError; override;
  157. end;
  158. TLZMAWorkerProcess = class(TLZMACompressorCustomWorker)
  159. private
  160. FProcess: THandle;
  161. FSharedMapping: THandle;
  162. FExeFilename: String;
  163. public
  164. constructor Create(const AEvents: PLZMACompressorSharedEvents); override;
  165. destructor Destroy; override;
  166. function GetExitHandle: THandle; override;
  167. procedure SetProps(const LZMA2: Boolean; const EncProps: TLZMAEncoderProps);
  168. override;
  169. procedure UnexpectedTerminationError; override;
  170. property ExeFilename: String read FExeFilename write FExeFilename;
  171. end;
  172. PLZMASeqInStream = ^TLZMASeqInStream;
  173. TLZMASeqInStream = record
  174. Read: function(p: PLZMASeqInStream; var buf; var size: Cardinal): TLZMASRes; stdcall;
  175. Instance: TLZMAWorkerThread;
  176. end;
  177. PLZMASeqOutStream = ^TLZMASeqOutStream;
  178. TLZMASeqOutStream = record
  179. Write: function(p: PLZMASeqOutStream; const buf; size: Cardinal): Cardinal; stdcall;
  180. Instance: TLZMAWorkerThread;
  181. end;
  182. PLZMACompressProgress = ^TLZMACompressProgress;
  183. TLZMACompressProgress = record
  184. Progress: function(p: PLZMACompressProgress; inSize, outSize: Integer64): TLZMASRes; stdcall;
  185. Instance: TLZMAWorkerThread;
  186. end;
  187. var
  188. LZMADLLInitialized: Boolean;
  189. LZMA_Init: function(LZMA2: BOOL; var handle: TLZMACompressorHandle): TLZMASRes;
  190. stdcall;
  191. LZMA_SetProps: function(handle: TLZMACompressorHandle;
  192. const encProps: TLZMAEncoderProps; encPropsSize: Cardinal): TLZMASRes; stdcall;
  193. LZMA_Encode: function(handle: TLZMACompressorHandle;
  194. const inStream: TLZMASeqInStream; const outStream: TLZMASeqOutStream;
  195. const progress: TLZMACompressProgress): TLZMASRes; stdcall;
  196. LZMA_End: function(handle: TLZMACompressorHandle): TLZMASRes; stdcall;
  197. const
  198. { SRes (TLZMASRes) }
  199. SZ_OK = 0;
  200. SZ_ERROR_MEM = 2;
  201. SZ_ERROR_READ = 8;
  202. SZ_ERROR_PROGRESS = 10;
  203. SZ_ERROR_FAIL = 11;
  204. function InterlockedExchangeAdd(var Addend: Longint; Value: Longint): Longint;
  205. stdcall; external kernel32;
  206. function GetNumberOfProcessors: Cardinal;
  207. var
  208. SysInfo: TSystemInfo;
  209. begin
  210. GetSystemInfo(SysInfo);
  211. Result := SysInfo.dwNumberOfProcessors;
  212. end;
  213. function LZMAInitCompressFunctions(Module: HMODULE): Boolean;
  214. begin
  215. LZMADLLInitialized := False;
  216. LZMA_Init := GetProcAddress(Module, 'LZMA_Init3');
  217. LZMA_SetProps := GetProcAddress(Module, 'LZMA_SetProps3');
  218. LZMA_Encode := GetProcAddress(Module, 'LZMA_Encode3');
  219. LZMA_End := GetProcAddress(Module, 'LZMA_End3');
  220. Result := Assigned(LZMA_Init) and Assigned(LZMA_SetProps) and
  221. Assigned(LZMA_Encode) and Assigned(LZMA_End);
  222. if Result then
  223. LZMADLLInitialized := True
  224. else begin
  225. LZMA_Init := nil;
  226. LZMA_SetProps := nil;
  227. LZMA_Encode := nil;
  228. LZMA_End := nil;
  229. end;
  230. end;
  231. procedure LZMAInternalError(const Msg: String);
  232. begin
  233. raise ECompressInternalError.Create('lzma: ' + Msg);
  234. end;
  235. procedure LZMAInternalErrorFmt(const Msg: String; const Args: array of const);
  236. begin
  237. LZMAInternalError(Format(Msg, Args));
  238. end;
  239. procedure LZMAWin32Error(const FunctionName: String);
  240. var
  241. LastError: DWORD;
  242. begin
  243. LastError := GetLastError;
  244. LZMAInternalErrorFmt('%s failed (%u)', [FunctionName, LastError]);
  245. end;
  246. function LZMAGetLevel(const Value: String; var Level: Integer): Boolean;
  247. begin
  248. Result := True;
  249. if CompareText(Value, 'fast') = 0 then
  250. Level := clLZMAFast
  251. else if CompareText(Value, 'normal') = 0 then
  252. Level := clLZMANormal
  253. else if CompareText(Value, 'max') = 0 then
  254. Level := clLZMAMax
  255. else if CompareText(Value, 'ultra') = 0 then
  256. Level := clLZMAUltra
  257. else if CompareText(Value, 'ultra64') = 0 then
  258. Level := clLZMAUltra64
  259. else
  260. Result := False;
  261. end;
  262. function LZMACreateEvent(const ManualReset: BOOL): THandle;
  263. begin
  264. Result := CreateEvent(nil, ManualReset, False, nil);
  265. if Result = 0 then
  266. LZMAWin32Error('CreateEvent');
  267. end;
  268. function LZMASeqInStreamReadWrapper(p: PLZMASeqInStream; var buf;
  269. var size: Cardinal): TLZMASRes; stdcall;
  270. begin
  271. if p.Instance.Read(buf, size, size) = S_OK then
  272. Result := SZ_OK
  273. else
  274. Result := SZ_ERROR_READ;
  275. end;
  276. function LZMASeqOutStreamWriteWrapper(p: PLZMASeqOutStream; const buf;
  277. size: Cardinal): Cardinal; stdcall;
  278. begin
  279. if p.Instance.Write(buf, size, Result) <> S_OK then
  280. Result := 0;
  281. end;
  282. function LZMACompressProgressProgressWrapper(p: PLZMACompressProgress;
  283. inSize, outSize: Integer64): TLZMASRes; stdcall;
  284. begin
  285. if p.Instance.ProgressMade(inSize) = S_OK then
  286. Result := SZ_OK
  287. else
  288. Result := SZ_ERROR_PROGRESS;
  289. end;
  290. { TLZMACompressorRingBuffer:
  291. Designed to support concurrent, lock-free access by two threads in a
  292. pipe-like fashion: one thread may read from the buffer (FIFO) at the same
  293. time another thread is writing to it. Two threads, however, may NOT both
  294. read, or both write simultaneously. }
  295. procedure RingBufferReset(var Ring: TLZMACompressorRingBuffer);
  296. begin
  297. Ring.Count := 0;
  298. Ring.WriterOffset := 0;
  299. Ring.ReaderOffset := 0;
  300. end;
  301. function RingBufferInternalWriteOrRead(var Ring: TLZMACompressorRingBuffer;
  302. const AWrite: Boolean; var Offset: Longint; const Data: Pointer;
  303. Size: Longint): Longint;
  304. var
  305. P: ^Byte;
  306. Bytes: Longint;
  307. begin
  308. Result := 0;
  309. P := Data;
  310. while Size > 0 do begin
  311. if AWrite then
  312. Bytes := SizeOf(Ring.Buf) - Ring.Count
  313. else
  314. Bytes := Ring.Count;
  315. if Bytes = 0 then
  316. { Buffer is full (write) or empty (read) }
  317. Break;
  318. if Bytes > Size then
  319. Bytes := Size;
  320. if Bytes > SizeOf(Ring.Buf) - Offset then
  321. Bytes := SizeOf(Ring.Buf) - Offset;
  322. if AWrite then begin
  323. Move(P^, Ring.Buf[Offset], Bytes);
  324. InterlockedExchangeAdd(Ring.Count, Bytes);
  325. end
  326. else begin
  327. Move(Ring.Buf[Offset], P^, Bytes);
  328. InterlockedExchangeAdd(Ring.Count, -Bytes);
  329. end;
  330. if Offset + Bytes = SizeOf(Ring.Buf) then
  331. Offset := 0
  332. else
  333. Inc(Offset, Bytes);
  334. Dec(Size, Bytes);
  335. Inc(Result, Bytes);
  336. Inc(P, Bytes);
  337. end;
  338. end;
  339. function RingBufferRead(var Ring: TLZMACompressorRingBuffer; var Buf;
  340. const Size: Longint): Longint;
  341. begin
  342. Result := RingBufferInternalWriteOrRead(Ring, False, Ring.ReaderOffset,
  343. @Buf, Size);
  344. end;
  345. function RingBufferWrite(var Ring: TLZMACompressorRingBuffer; const Buf;
  346. const Size: Longint): Longint;
  347. begin
  348. Result := RingBufferInternalWriteOrRead(Ring, True, Ring.WriterOffset,
  349. @Buf, Size);
  350. end;
  351. function RingBufferReadToCallback(var Ring: TLZMACompressorRingBuffer;
  352. const AWriteProc: TCompressorWriteProc; Size: Longint): Longint;
  353. var
  354. Bytes: Longint;
  355. begin
  356. Result := 0;
  357. while Size > 0 do begin
  358. Bytes := Ring.Count;
  359. if Bytes = 0 then
  360. Break;
  361. if Bytes > Size then
  362. Bytes := Size;
  363. if Bytes > SizeOf(Ring.Buf) - Ring.ReaderOffset then
  364. Bytes := SizeOf(Ring.Buf) - Ring.ReaderOffset;
  365. AWriteProc(Ring.Buf[Ring.ReaderOffset], Bytes);
  366. InterlockedExchangeAdd(Ring.Count, -Bytes);
  367. if Ring.ReaderOffset + Bytes = SizeOf(Ring.Buf) then
  368. Ring.ReaderOffset := 0
  369. else
  370. Inc(Ring.ReaderOffset, Bytes);
  371. Dec(Size, Bytes);
  372. Inc(Result, Bytes);
  373. end;
  374. end;
  375. { TLZMACompressorProps }
  376. constructor TLZMACompressorProps.Create;
  377. begin
  378. inherited;
  379. Algorithm := -1;
  380. BTMode := -1;
  381. end;
  382. { TLZMACompressorCustomWorker }
  383. constructor TLZMACompressorCustomWorker.Create(const AEvents: PLZMACompressorSharedEvents);
  384. begin
  385. inherited Create;
  386. FEvents := AEvents;
  387. end;
  388. { TLZMAWorkerThread }
  389. function WorkerThreadFunc(Parameter: Pointer): Integer;
  390. begin
  391. try
  392. TLZMAWorkerThread(Parameter).WorkerThreadProc;
  393. except
  394. end;
  395. Result := 0;
  396. end;
  397. constructor TLZMAWorkerThread.Create(const AEvents: PLZMACompressorSharedEvents);
  398. begin
  399. inherited;
  400. FShared := VirtualAlloc(nil, SizeOf(FShared^), MEM_COMMIT, PAGE_READWRITE);
  401. if FShared = nil then
  402. OutOfMemoryError;
  403. end;
  404. destructor TLZMAWorkerThread.Destroy;
  405. begin
  406. if FThread <> 0 then begin
  407. SetEvent(FEvents.TerminateWorkerEvent);
  408. WaitForSingleObject(FThread, INFINITE);
  409. CloseHandle(FThread);
  410. FThread := 0;
  411. end;
  412. if Assigned(FLZMAHandle) then
  413. LZMA_End(FLZMAHandle);
  414. if Assigned(FShared) then
  415. VirtualFree(FShared, 0, MEM_RELEASE);
  416. inherited;
  417. end;
  418. function TLZMAWorkerThread.GetExitHandle: THandle;
  419. begin
  420. Result := FThread;
  421. end;
  422. procedure TLZMAWorkerThread.SetProps(const LZMA2: Boolean;
  423. const EncProps: TLZMAEncoderProps);
  424. var
  425. Res: TLZMASRes;
  426. ThreadID: DWORD;
  427. begin
  428. Res := LZMA_Init(LZMA2, FLZMAHandle);
  429. if Res = SZ_ERROR_MEM then
  430. OutOfMemoryError;
  431. if Res <> SZ_OK then
  432. LZMAInternalErrorFmt('LZMA_Init failed with code %d', [Res]);
  433. if LZMA_SetProps(FLZMAHandle, EncProps, SizeOf(EncProps)) <> SZ_OK then
  434. LZMAInternalError('LZMA_SetProps failed');
  435. FThread := BeginThread(nil, 0, WorkerThreadFunc, Self, 0, ThreadID);
  436. if FThread = 0 then
  437. LZMAWin32Error('BeginThread');
  438. end;
  439. procedure TLZMAWorkerThread.UnexpectedTerminationError;
  440. begin
  441. LZMAInternalError('Worker thread terminated unexpectedly');
  442. end;
  443. procedure TLZMAWorkerThread.WorkerThreadProc;
  444. { Worker thread main procedure }
  445. var
  446. InStream: TLZMASeqInStream;
  447. OutStream: TLZMASeqOutStream;
  448. CompressProgress: TLZMACompressProgress;
  449. H: array[0..1] of THandle;
  450. begin
  451. InStream.Read := LZMASeqInStreamReadWrapper;
  452. InStream.Instance := Self;
  453. OutStream.Write := LZMASeqOutStreamWriteWrapper;
  454. OutStream.Instance := Self;
  455. CompressProgress.Progress := LZMACompressProgressProgressWrapper;
  456. CompressProgress.Instance := Self;
  457. H[0] := FEvents.TerminateWorkerEvent;
  458. H[1] := FEvents.StartEncodeEvent;
  459. while WaitForMultipleObjects(2, @H, False, INFINITE) = WAIT_OBJECT_0 + 1 do begin
  460. FShared.EncodeResult := LZMA_Encode(FLZMAHandle, InStream, OutStream,
  461. CompressProgress);
  462. if not SetEvent(FEvents.WorkerEncodeFinishedEvent) then
  463. Break;
  464. end;
  465. end;
  466. function TLZMAWorkerThread.WakeMainAndWaitUntil(const AWakeEvent,
  467. AWaitEvent: THandle): HRESULT;
  468. var
  469. H: array[0..1] of THandle;
  470. begin
  471. if not SetEvent(AWakeEvent) then begin
  472. SetEvent(FEvents.TerminateWorkerEvent);
  473. Result := E_FAIL;
  474. Exit;
  475. end;
  476. H[0] := FEvents.TerminateWorkerEvent;
  477. H[1] := AWaitEvent;
  478. case WaitForMultipleObjects(2, @H, False, INFINITE) of
  479. WAIT_OBJECT_0 + 0: Result := E_ABORT;
  480. WAIT_OBJECT_0 + 1: Result := S_OK;
  481. else
  482. SetEvent(FEvents.TerminateWorkerEvent);
  483. Result := E_FAIL;
  484. end;
  485. end;
  486. function TLZMAWorkerThread.FillBuffer(const AWrite: Boolean;
  487. const Data: Pointer; Size: Cardinal; var ProcessedSize: Cardinal): HRESULT;
  488. { Called from worker thread (or a thread spawned by the worker thread) }
  489. var
  490. P: ^Byte;
  491. Bytes: Longint;
  492. begin
  493. ProcessedSize := 0;
  494. if Size > Cardinal(High(Longint)) then begin
  495. Result := E_INVALIDARG;
  496. Exit;
  497. end;
  498. P := Data;
  499. while Size <> 0 do begin
  500. if AWrite then
  501. Bytes := RingBufferWrite(FShared.OutputBuffer, P^, Size)
  502. else
  503. Bytes := RingBufferRead(FShared.InputBuffer, P^, Size);
  504. if Bytes = 0 then begin
  505. if AWrite then begin
  506. { Output buffer full; wait for the main thread to flush it }
  507. Result := WakeMainAndWaitUntil(FEvents.WorkerWaitingOnOutputEvent,
  508. FEvents.EndWaitOnOutputEvent);
  509. if Result <> S_OK then
  510. Exit;
  511. end
  512. else begin
  513. { Input buffer empty; wait for the main thread to fill it }
  514. if FShared.NoMoreInput then
  515. Break;
  516. Result := WakeMainAndWaitUntil(FEvents.WorkerWaitingOnInputEvent,
  517. FEvents.EndWaitOnInputEvent);
  518. if Result <> S_OK then
  519. Exit;
  520. end;
  521. end
  522. else begin
  523. Inc(ProcessedSize, Bytes);
  524. Dec(Size, Bytes);
  525. Inc(P, Bytes);
  526. end;
  527. end;
  528. Result := S_OK;
  529. end;
  530. function TLZMAWorkerThread.Read(var Data; Size: Cardinal;
  531. var ProcessedSize: Cardinal): HRESULT;
  532. { Called from worker thread (or a thread spawned by the worker thread) }
  533. begin
  534. { Sanity check: Make sure we're the only thread inside Read }
  535. if InterlockedExchange(FReadLock, 1) <> 0 then begin
  536. Result := E_FAIL;
  537. Exit;
  538. end;
  539. Result := FillBuffer(False, @Data, Size, ProcessedSize);
  540. FReadLock := 0;
  541. end;
  542. function TLZMAWorkerThread.Write(const Data; Size: Cardinal;
  543. var ProcessedSize: Cardinal): HRESULT;
  544. { Called from worker thread (or a thread spawned by the worker thread) }
  545. begin
  546. { Sanity check: Make sure we're the only thread inside Write }
  547. if InterlockedExchange(FWriteLock, 1) <> 0 then begin
  548. Result := E_FAIL;
  549. Exit;
  550. end;
  551. Result := FillBuffer(True, @Data, Size, ProcessedSize);
  552. FWriteLock := 0;
  553. end;
  554. function TLZMAWorkerThread.ProgressMade(const TotalBytesProcessed: Integer64): HRESULT;
  555. { Called from worker thread (or a thread spawned by the worker thread) }
  556. var
  557. T: DWORD;
  558. KBProcessed: Integer64;
  559. begin
  560. T := GetTickCount;
  561. if Cardinal(T - FLastProgressTick) >= Cardinal(100) then begin
  562. { Sanity check: Make sure we're the only thread inside Progress }
  563. if InterlockedExchange(FProgressLock, 1) <> 0 then begin
  564. Result := E_FAIL;
  565. Exit;
  566. end;
  567. FLastProgressTick := T;
  568. { Make sure TotalBytesProcessed isn't negative. LZMA's Types.h says
  569. "-1 for size means unknown value", though I don't see any place
  570. where LzmaEnc actually does call Progress with inSize = -1. }
  571. if Longint(TotalBytesProcessed.Hi) >= 0 then begin
  572. KBProcessed := TotalBytesProcessed;
  573. Div64(KBProcessed, 1024);
  574. FShared.ProgressKB := KBProcessed.Lo;
  575. end;
  576. Result := WakeMainAndWaitUntil(FEvents.WorkerHasProgressEvent,
  577. FEvents.EndWaitOnProgressEvent);
  578. FProgressLock := 0;
  579. end
  580. else
  581. Result := S_OK;
  582. end;
  583. { TLZMAWorkerProcess }
  584. constructor TLZMAWorkerProcess.Create(const AEvents: PLZMACompressorSharedEvents);
  585. begin
  586. inherited;
  587. FSharedMapping := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
  588. PAGE_READWRITE, 0, SizeOf(FShared^), nil);
  589. if FSharedMapping = 0 then
  590. LZMAWin32Error('CreateFileMapping');
  591. FShared := MapViewOfFile(FSharedMapping, FILE_MAP_WRITE, 0, 0,
  592. SizeOf(FShared^));
  593. if FShared = nil then
  594. LZMAWin32Error('MapViewOfFile');
  595. end;
  596. destructor TLZMAWorkerProcess.Destroy;
  597. begin
  598. if FProcess <> 0 then begin
  599. SetEvent(FEvents.TerminateWorkerEvent);
  600. WaitForSingleObject(FProcess, INFINITE);
  601. CloseHandle(FProcess);
  602. FProcess := 0;
  603. end;
  604. if Assigned(FShared) then
  605. UnmapViewOfFile(FShared);
  606. if FSharedMapping <> 0 then
  607. CloseHandle(FSharedMapping);
  608. inherited;
  609. end;
  610. function TLZMAWorkerProcess.GetExitHandle: THandle;
  611. begin
  612. Result := FProcess;
  613. end;
  614. procedure TLZMAWorkerProcess.SetProps(const LZMA2: Boolean;
  615. const EncProps: TLZMAEncoderProps);
  616. function GetSystemDir: String;
  617. var
  618. Buf: array[0..MAX_PATH-1] of Char;
  619. begin
  620. GetSystemDirectory(Buf, SizeOf(Buf) div SizeOf(Buf[0]));
  621. Result := Buf;
  622. end;
  623. procedure DupeHandle(const SourceHandle: THandle; const DestProcess: THandle;
  624. var DestHandle: THandle; const DesiredAccess: DWORD);
  625. begin
  626. if not DuplicateHandle(GetCurrentProcess, SourceHandle, DestProcess,
  627. @DestHandle, DesiredAccess, False, 0) then
  628. LZMAWin32Error('DuplicateHandle');
  629. end;
  630. procedure DupeEventHandles(const Src: TLZMACompressorSharedEvents;
  631. const Process: THandle; var Dest: TLZMACompressorSharedEvents);
  632. procedure DupeEvent(const SourceHandle: THandle; var DestHandle: THandle);
  633. begin
  634. DupeHandle(SourceHandle, Process, DestHandle, SYNCHRONIZE or
  635. EVENT_MODIFY_STATE);
  636. end;
  637. begin
  638. DupeEvent(Src.TerminateWorkerEvent, Dest.TerminateWorkerEvent);
  639. DupeEvent(Src.StartEncodeEvent, Dest.StartEncodeEvent);
  640. DupeEvent(Src.EndWaitOnInputEvent, Dest.EndWaitOnInputEvent);
  641. DupeEvent(Src.EndWaitOnOutputEvent, Dest.EndWaitOnOutputEvent);
  642. DupeEvent(Src.EndWaitOnProgressEvent, Dest.EndWaitOnProgressEvent);
  643. DupeEvent(Src.WorkerWaitingOnInputEvent, Dest.WorkerWaitingOnInputEvent);
  644. DupeEvent(Src.WorkerWaitingOnOutputEvent, Dest.WorkerWaitingOnOutputEvent);
  645. DupeEvent(Src.WorkerHasProgressEvent, Dest.WorkerHasProgressEvent);
  646. DupeEvent(Src.WorkerEncodeFinishedEvent, Dest.WorkerEncodeFinishedEvent);
  647. end;
  648. const
  649. InheritableSecurity: TSecurityAttributes = (
  650. nLength: SizeOf(InheritableSecurity); lpSecurityDescriptor: nil;
  651. bInheritHandle: True);
  652. var
  653. ProcessDataMapping: THandle;
  654. ProcessData: PLZMACompressorProcessData;
  655. StartupInfo: TStartupInfo;
  656. ProcessInfo: TProcessInformation;
  657. begin
  658. ProcessData := nil;
  659. ProcessDataMapping := CreateFileMapping(INVALID_HANDLE_VALUE,
  660. @InheritableSecurity, PAGE_READWRITE, 0, SizeOf(ProcessData^), nil);
  661. if ProcessDataMapping = 0 then
  662. LZMAWin32Error('CreateFileMapping');
  663. try
  664. ProcessData := MapViewOfFile(ProcessDataMapping, FILE_MAP_WRITE, 0, 0,
  665. SizeOf(ProcessData^));
  666. if ProcessData = nil then
  667. LZMAWin32Error('MapViewOfFile');
  668. ProcessData.StructSize := SizeOf(ProcessData^);
  669. ProcessData.LZMA2 := LZMA2;
  670. ProcessData.EncoderProps := EncProps;
  671. ProcessData.SharedDataStructSize := SizeOf(FShared^);
  672. FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  673. StartupInfo.cb := SizeOf(StartupInfo);
  674. StartupInfo.dwFlags := STARTF_FORCEOFFFEEDBACK;
  675. if not CreateProcess(PChar(FExeFilename),
  676. PChar(Format('islzma_exe %d 0x%x', [ISLZMA_EXE_VERSION, ProcessDataMapping])),
  677. nil, nil, True, CREATE_DEFAULT_ERROR_MODE or CREATE_SUSPENDED, nil,
  678. PChar(GetSystemDir), StartupInfo, ProcessInfo) then
  679. LZMAWin32Error('CreateProcess');
  680. try
  681. { We duplicate the handles instead of using inheritable handles so that
  682. if something outside this unit calls CreateProcess() while compression
  683. is in progress, our handles won't be inadvertently passed on to that
  684. other process. }
  685. DupeHandle(GetCurrentProcess, ProcessInfo.hProcess,
  686. ProcessData.ParentProcess, SYNCHRONIZE);
  687. DupeHandle(FSharedMapping, ProcessInfo.hProcess,
  688. ProcessData.SharedDataMapping, FILE_MAP_WRITE);
  689. DupeEventHandles(FEvents^, ProcessInfo.hProcess, ProcessData.Events);
  690. if ResumeThread(ProcessInfo.hThread) = DWORD(-1) then
  691. LZMAWin32Error('ResumeThread');
  692. except
  693. CloseHandle(ProcessInfo.hThread);
  694. TerminateProcess(ProcessInfo.hProcess, 1);
  695. WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  696. CloseHandle(ProcessInfo.hProcess);
  697. raise;
  698. end;
  699. FProcess := ProcessInfo.hProcess;
  700. CloseHandle(ProcessInfo.hThread);
  701. finally
  702. if Assigned(ProcessData) then
  703. UnmapViewOfFile(ProcessData);
  704. CloseHandle(ProcessDataMapping);
  705. end;
  706. end;
  707. procedure TLZMAWorkerProcess.UnexpectedTerminationError;
  708. var
  709. ProcessExitCode: DWORD;
  710. begin
  711. if GetExitCodeProcess(FProcess, ProcessExitCode) then
  712. LZMAInternalErrorFmt('Worker process terminated unexpectedly (0x%x)',
  713. [ProcessExitCode])
  714. else
  715. LZMAInternalError('Worker process terminated unexpectedly ' +
  716. '(failed to get exit code)');
  717. end;
  718. { TLZMACompressor }
  719. constructor TLZMACompressor.Create(AWriteProc: TCompressorWriteProc;
  720. AProgressProc: TCompressorProgressProc; CompressionLevel: Integer;
  721. ACompressorProps: TCompressorProps);
  722. begin
  723. inherited;
  724. FEvents.TerminateWorkerEvent := LZMACreateEvent(True); { manual reset }
  725. FEvents.StartEncodeEvent := LZMACreateEvent(False); { auto reset }
  726. FEvents.EndWaitOnInputEvent := LZMACreateEvent(False); { auto reset }
  727. FEvents.EndWaitOnOutputEvent := LZMACreateEvent(False); { auto reset }
  728. FEvents.EndWaitOnProgressEvent := LZMACreateEvent(False); { auto reset }
  729. FEvents.WorkerWaitingOnInputEvent := LZMACreateEvent(True); { manual reset }
  730. FEvents.WorkerWaitingOnOutputEvent := LZMACreateEvent(True); { manual reset }
  731. FEvents.WorkerHasProgressEvent := LZMACreateEvent(True); { manual reset }
  732. FEvents.WorkerEncodeFinishedEvent := LZMACreateEvent(True); { manual reset }
  733. InitializeProps(CompressionLevel, ACompressorProps);
  734. end;
  735. destructor TLZMACompressor.Destroy;
  736. procedure DestroyEvent(const AEvent: THandle);
  737. begin
  738. if AEvent <> 0 then
  739. CloseHandle(AEvent);
  740. end;
  741. begin
  742. FWorker.Free;
  743. DestroyEvent(FEvents.WorkerEncodeFinishedEvent);
  744. DestroyEvent(FEvents.WorkerHasProgressEvent);
  745. DestroyEvent(FEvents.WorkerWaitingOnOutputEvent);
  746. DestroyEvent(FEvents.WorkerWaitingOnInputEvent);
  747. DestroyEvent(FEvents.EndWaitOnProgressEvent);
  748. DestroyEvent(FEvents.EndWaitOnOutputEvent);
  749. DestroyEvent(FEvents.EndWaitOnInputEvent);
  750. DestroyEvent(FEvents.StartEncodeEvent);
  751. DestroyEvent(FEvents.TerminateWorkerEvent);
  752. inherited;
  753. end;
  754. procedure TLZMACompressor.InitializeProps(const CompressionLevel: Integer;
  755. const ACompressorProps: TCompressorProps);
  756. const
  757. algorithm: array [clLZMAFast..clLZMAUltra64] of Cardinal = (0, 1, 1, 1, 1);
  758. dicSize: array [clLZMAFast..clLZMAUltra64] of Cardinal = (32 shl 10, 2 shl 20, 8 shl 20, 32 shl 20, 64 shl 20);
  759. numFastBytes: array [clLZMAFast..clLZMAUltra64] of Cardinal = (32, 32, 64, 64, 64);
  760. btMode: array [clLZMAFast..clLZMAUltra64] of Cardinal = (0, 1, 1, 1, 1);
  761. var
  762. EncProps: TLZMAEncoderProps;
  763. Props: TLZMACompressorProps;
  764. WorkerProcessFilename: String;
  765. begin
  766. if (CompressionLevel < Low(algorithm)) or (CompressionLevel > High(algorithm)) then
  767. LZMAInternalError('TLZMACompressor.Create got invalid CompressionLevel ' + IntToStr(CompressionLevel));
  768. FillChar(EncProps, SizeOf(EncProps), 0);
  769. EncProps.Algorithm := algorithm[CompressionLevel];
  770. EncProps.BTMode := btMode[CompressionLevel];
  771. EncProps.DictionarySize := dicSize[CompressionLevel];
  772. EncProps.NumBlockThreads := -1;
  773. EncProps.NumFastBytes := numFastBytes[CompressionLevel];
  774. EncProps.NumThreads := -1;
  775. if ACompressorProps is TLZMACompressorProps then begin
  776. Props := (ACompressorProps as TLZMACompressorProps);
  777. if Props.Algorithm <> -1 then
  778. EncProps.Algorithm := Props.Algorithm;
  779. EncProps.BlockSize := Props.BlockSize;
  780. if Props.BTMode <> -1 then
  781. EncProps.BTMode := Props.BTMode;
  782. if Props.DictionarySize <> 0 then
  783. EncProps.DictionarySize := Props.DictionarySize;
  784. if Props.NumBlockThreads <> 0 then
  785. EncProps.NumBlockThreads := Props.NumBlockThreads;
  786. if Props.NumFastBytes <> 0 then
  787. EncProps.NumFastBytes := Props.NumFastBytes;
  788. if Props.NumThreads <> 0 then
  789. EncProps.NumThreads := Props.NumThreads;
  790. WorkerProcessFilename := Props.WorkerProcessFilename;
  791. end;
  792. if WorkerProcessFilename <> '' then begin
  793. FWorker := TLZMAWorkerProcess.Create(@FEvents);
  794. (FWorker as TLZMAWorkerProcess).ExeFilename := WorkerProcessFilename;
  795. end
  796. else begin
  797. if not LZMADLLInitialized then
  798. LZMAInternalError('LZMA DLL functions not initialized');
  799. FWorker := TLZMAWorkerThread.Create(@FEvents);
  800. end;
  801. FShared := FWorker.FShared;
  802. FWorker.SetProps(FUseLZMA2, EncProps);
  803. end;
  804. class function TLZMACompressor.IsEventSet(const AEvent: THandle): Boolean;
  805. begin
  806. Result := False;
  807. case WaitForSingleObject(AEvent, 0) of
  808. WAIT_OBJECT_0: Result := True;
  809. WAIT_TIMEOUT: ;
  810. else
  811. LZMAInternalError('IsEventSet: WaitForSingleObject failed');
  812. end;
  813. end;
  814. class procedure TLZMACompressor.SatisfyWorkerWait(const AWorkerEvent,
  815. AMainEvent: THandle);
  816. begin
  817. if IsEventSet(AWorkerEvent) then begin
  818. if not ResetEvent(AWorkerEvent) then
  819. LZMAWin32Error('SatisfyWorkerWait: ResetEvent');
  820. if not SetEvent(AMainEvent) then
  821. LZMAWin32Error('SatisfyWorkerWait: SetEvent');
  822. end;
  823. end;
  824. procedure TLZMACompressor.SatisfyWorkerWaitOnInput;
  825. begin
  826. SatisfyWorkerWait(FEvents.WorkerWaitingOnInputEvent, FEvents.EndWaitOnInputEvent);
  827. end;
  828. procedure TLZMACompressor.SatisfyWorkerWaitOnOutput;
  829. begin
  830. SatisfyWorkerWait(FEvents.WorkerWaitingOnOutputEvent, FEvents.EndWaitOnOutputEvent);
  831. end;
  832. procedure TLZMACompressor.UpdateProgress;
  833. var
  834. NewProgressKB: LongWord;
  835. Bytes: Integer64;
  836. begin
  837. if IsEventSet(FEvents.WorkerHasProgressEvent) then begin
  838. if Assigned(ProgressProc) then begin
  839. NewProgressKB := FShared.ProgressKB;
  840. Bytes.Hi := 0;
  841. Bytes.Lo := NewProgressKB - FLastProgressKB; { wraparound is OK }
  842. Mul64(Bytes, 1024);
  843. FLastProgressKB := NewProgressKB;
  844. while Bytes.Hi <> 0 do begin
  845. ProgressProc(Cardinal($80000000));
  846. ProgressProc(Cardinal($80000000));
  847. Dec(Bytes.Hi);
  848. end;
  849. ProgressProc(Bytes.Lo);
  850. end;
  851. if not ResetEvent(FEvents.WorkerHasProgressEvent) then
  852. LZMAWin32Error('UpdateProgress: ResetEvent');
  853. if not SetEvent(FEvents.EndWaitOnProgressEvent) then
  854. LZMAWin32Error('UpdateProgress: SetEvent');
  855. end;
  856. end;
  857. procedure TLZMACompressor.FlushOutputBuffer(const OnlyOptimalSize: Boolean);
  858. const
  859. { Calling WriteProc may be an expensive operation, so we prefer to wait
  860. until we've accumulated a reasonable number of bytes before flushing }
  861. OptimalFlushSize = $10000; { can't exceed size of OutputBuffer.Buf }
  862. var
  863. Bytes: Longint;
  864. begin
  865. while True do begin
  866. Bytes := FShared.OutputBuffer.Count;
  867. if Bytes = 0 then
  868. Break;
  869. if Bytes > OptimalFlushSize then
  870. Bytes := OptimalFlushSize;
  871. if OnlyOptimalSize and (Bytes < OptimalFlushSize) then
  872. Break;
  873. RingBufferReadToCallback(FShared.OutputBuffer, WriteProc, Bytes);
  874. { Output buffer (partially?) flushed; unblock worker Write }
  875. SatisfyWorkerWaitOnOutput;
  876. end;
  877. { Must satisfy a waiting worker even if there was nothing to flush. (Needed
  878. to avoid deadlock in the event the main thread empties the output buffer
  879. after the worker's FillBuffer(AWrite=True) gets Bytes=0 but *before* it
  880. sets WorkerWaitingOnOutputEvent and waits on EndWaitOnOutputEvent.) }
  881. SatisfyWorkerWaitOnOutput;
  882. end;
  883. procedure TLZMACompressor.StartEncode;
  884. begin
  885. if not FEncodeStarted then begin
  886. FShared.NoMoreInput := False;
  887. FShared.ProgressKB := 0;
  888. FShared.EncodeResult := -1;
  889. RingBufferReset(FShared.InputBuffer);
  890. RingBufferReset(FShared.OutputBuffer);
  891. FLastInputWriteCount := 0;
  892. FLastProgressKB := 0;
  893. FEncodeFinished := False;
  894. FEncodeStarted := True;
  895. if not ResetEvent(FEvents.WorkerEncodeFinishedEvent) then
  896. LZMAWin32Error('StartEncode: ResetEvent');
  897. if not SetEvent(FEvents.StartEncodeEvent) then
  898. LZMAWin32Error('StartEncode: SetEvent');
  899. end;
  900. end;
  901. procedure TLZMACompressor.WaitForWorkerEvent;
  902. var
  903. H: array[0..4] of THandle;
  904. begin
  905. { Wait until the worker needs our attention. Separate, manual-reset events
  906. are used for progress/input/output because it allows us to see
  907. specifically what the worker is waiting for, which eases debugging and
  908. helps to avoid unnecessary wakeups.
  909. Note that the order of the handles in the array is significant: when more
  910. than one object is signaled, WaitForMultipleObjects returns the index of
  911. the array's first signaled object. The "worker unexpectedly terminated"
  912. object must be at the front to ensure it takes precedence over the Worker*
  913. events. }
  914. H[0] := FWorker.GetExitHandle;
  915. H[1] := FEvents.WorkerEncodeFinishedEvent;
  916. H[2] := FEvents.WorkerHasProgressEvent;
  917. H[3] := FEvents.WorkerWaitingOnInputEvent;
  918. H[4] := FEvents.WorkerWaitingOnOutputEvent;
  919. case WaitForMultipleObjects(5, @H, False, INFINITE) of
  920. WAIT_OBJECT_0 + 0: FWorker.UnexpectedTerminationError;
  921. WAIT_OBJECT_0 + 1: FEncodeFinished := True;
  922. WAIT_OBJECT_0 + 2,
  923. WAIT_OBJECT_0 + 3,
  924. WAIT_OBJECT_0 + 4: ;
  925. else
  926. LZMAInternalError('WaitForWorkerEvent: WaitForMultipleObjects failed');
  927. end;
  928. end;
  929. procedure TLZMACompressor.DoCompress(const Buffer; Count: Longint);
  930. var
  931. P: ^Byte;
  932. BytesWritten: Longint;
  933. InputWriteCount: LongWord;
  934. begin
  935. StartEncode;
  936. P := @Buffer;
  937. while Count > 0 do begin
  938. if FEncodeFinished then begin
  939. if FShared.EncodeResult = SZ_ERROR_MEM then
  940. OutOfMemoryError;
  941. LZMAInternalErrorFmt('Compress: LZMA_Encode failed with code %d',
  942. [FShared.EncodeResult]);
  943. end;
  944. UpdateProgress;
  945. { Note that the progress updates that come in every ~100 ms also serve to
  946. keep the output buffer flushed well before it fills up. }
  947. FlushOutputBuffer(True);
  948. BytesWritten := RingBufferWrite(FShared.InputBuffer, P^, Count);
  949. if BytesWritten = 0 then begin
  950. { Input buffer full; unblock worker Read }
  951. SatisfyWorkerWaitOnInput;
  952. { Wait until the worker wants more input, needs output to be flushed,
  953. and/or has progress to report. All combinations are possible, so we
  954. need to handle all three before waiting again. }
  955. WaitForWorkerEvent;
  956. end
  957. else begin
  958. Dec(Count, BytesWritten);
  959. Inc(P, BytesWritten);
  960. { Unblock the worker every 64 KB so it doesn't have to wait until the
  961. entire input buffer is filled to begin/continue compressing. }
  962. InputWriteCount := FLastInputWriteCount + LongWord(BytesWritten);
  963. if InputWriteCount shr 16 <> FLastInputWriteCount shr 16 then
  964. SatisfyWorkerWaitOnInput;
  965. FLastInputWriteCount := InputWriteCount;
  966. end;
  967. end;
  968. end;
  969. procedure TLZMACompressor.DoFinish;
  970. begin
  971. StartEncode;
  972. FShared.NoMoreInput := True;
  973. while not FEncodeFinished do begin
  974. SatisfyWorkerWaitOnInput;
  975. UpdateProgress;
  976. FlushOutputBuffer(True);
  977. { Wait until the worker wants more input, needs output to be flushed,
  978. and/or has progress to report. All combinations are possible, so we
  979. need to handle all three before waiting again. }
  980. WaitForWorkerEvent;
  981. end;
  982. { Flush any remaining output in optimally-sized blocks, then flush whatever
  983. is left }
  984. FlushOutputBuffer(True);
  985. FlushOutputBuffer(False);
  986. case FShared.EncodeResult of
  987. SZ_OK: ;
  988. SZ_ERROR_MEM: OutOfMemoryError;
  989. else
  990. LZMAInternalErrorFmt('Finish: LZMA_Encode failed with code %d',
  991. [FShared.EncodeResult]);
  992. end;
  993. FEncodeStarted := False;
  994. end;
  995. { TLZMA2Compressor }
  996. constructor TLZMA2Compressor.Create(AWriteProc: TCompressorWriteProc;
  997. AProgressProc: TCompressorProgressProc; CompressionLevel: Integer;
  998. ACompressorProps: TCompressorProps);
  999. begin
  1000. FUseLZMA2 := True;
  1001. inherited;
  1002. end;
  1003. end.