islzma_exe.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*
  2. Inno Setup
  3. Copyright (C) 1997-2024 Jordan Russell
  4. Portions by Martijn Laan
  5. For conditions of distribution and use, see LICENSE.TXT.
  6. External EXE-based LZMA encoder
  7. Structures and functions in this file are derived from
  8. LZMA.pas revision 1.49.2.3.
  9. Intentional deviations from the original Pascal code:
  10. - The WaitForMultipleObjects() calls in WakeMainAndWaitUntil,
  11. CheckTerminateWorkerEvent, and BeginEncode additionally wait on
  12. ProcessData.ParentProcess.
  13. Everything else *should* be 100% consistent.
  14. */
  15. #include <windows.h>
  16. #include <shlwapi.h>
  17. #include "../../../../Components/Lzma2/7zTypes.h"
  18. #include "islzma.h"
  19. #define ISLZMA_EXE_VERSION 102
  20. typedef BYTE Byte;
  21. typedef LONG Longint;
  22. typedef ULONG LongWord;
  23. typedef LongWord THandle32;
  24. #define THandle32ToHandle(h) ULongToHandle(h)
  25. struct TLZMACompressorRingBuffer {
  26. volatile Longint Count; // updated by reader and writer using InterlockedExchangeAdd only
  27. Longint WriterOffset; // accessed only by writer thread
  28. Longint ReaderOffset; // accessed only by reader thread
  29. Byte Buf[0x100000];
  30. };
  31. struct TLZMACompressorSharedEvents {
  32. THandle32 TerminateWorkerEvent;
  33. THandle32 StartEncodeEvent;
  34. THandle32 EndWaitOnInputEvent;
  35. THandle32 EndWaitOnOutputEvent;
  36. THandle32 WorkerWaitingOnInputEvent;
  37. THandle32 WorkerWaitingOnOutputEvent;
  38. THandle32 WorkerEncodeFinishedEvent;
  39. };
  40. struct TLZMACompressorSharedData {
  41. volatile Int64 ProgressBytes;
  42. volatile BOOL NoMoreInput;
  43. volatile SRes EncodeResult;
  44. struct TLZMACompressorRingBuffer InputBuffer;
  45. struct TLZMACompressorRingBuffer OutputBuffer;
  46. };
  47. struct TLZMACompressorProcessData {
  48. LongWord StructSize;
  49. THandle32 ParentProcess;
  50. BOOL LZMA2;
  51. struct LZMAEncoderProps EncoderProps;
  52. struct TLZMACompressorSharedEvents Events;
  53. LongWord SharedDataStructSize;
  54. THandle32 SharedDataMapping;
  55. };
  56. static struct TLZMACompressorProcessData ProcessData;
  57. static struct TLZMACompressorSharedEvents *FEvents;
  58. static struct TLZMACompressorSharedData *FShared;
  59. static volatile LONG FReadLock, FWriteLock, FProgressLock;
  60. static Longint RingBufferInternalWriteOrRead(struct TLZMACompressorRingBuffer *Ring,
  61. const BOOL AWrite, Longint *Offset, void *Data, Longint Size)
  62. {
  63. Byte *P = Data;
  64. Longint Bytes;
  65. Longint Result = 0;
  66. while (Size > 0) {
  67. if (AWrite) {
  68. Bytes = sizeof(Ring->Buf) - Ring->Count;
  69. } else {
  70. Bytes = Ring->Count;
  71. }
  72. if (Bytes == 0) {
  73. /* Buffer is full (write) or empty (read) */
  74. break;
  75. }
  76. if (Bytes > Size) {
  77. Bytes = Size;
  78. }
  79. if (Bytes > (Longint)sizeof(Ring->Buf) - *Offset) {
  80. Bytes = (Longint)sizeof(Ring->Buf) - *Offset;
  81. }
  82. /* On a weakly-ordered CPU, the read of Count above must happen before
  83. Buf content is read below (otherwise the content could be stale) */
  84. MemoryBarrier();
  85. if (AWrite) {
  86. memcpy(&Ring->Buf[*Offset], P, Bytes);
  87. InterlockedExchangeAdd(&Ring->Count, Bytes); /* full barrier */
  88. } else {
  89. memcpy(P, &Ring->Buf[*Offset], Bytes);
  90. InterlockedExchangeAdd(&Ring->Count, -Bytes); /* full barrier */
  91. }
  92. if (*Offset + Bytes == sizeof(Ring->Buf)) {
  93. *Offset = 0;
  94. } else {
  95. *Offset += Bytes;
  96. }
  97. Size -= Bytes;
  98. Result += Bytes;
  99. P += Bytes;
  100. }
  101. return Result;
  102. }
  103. static Longint RingBufferRead(struct TLZMACompressorRingBuffer *Ring,
  104. void *Buf, Longint Size)
  105. {
  106. return RingBufferInternalWriteOrRead(Ring, FALSE, &Ring->ReaderOffset,
  107. Buf, Size);
  108. }
  109. static Longint RingBufferWrite(struct TLZMACompressorRingBuffer *Ring,
  110. void *Buf, Longint Size)
  111. {
  112. return RingBufferInternalWriteOrRead(Ring, TRUE, &Ring->WriterOffset,
  113. Buf, Size);
  114. }
  115. static HRESULT WakeMainAndWaitUntil(HANDLE AWakeEvent, HANDLE AWaitEvent)
  116. {
  117. HANDLE H[3];
  118. if (!SetEvent(AWakeEvent)) {
  119. SetEvent(THandle32ToHandle(FEvents->TerminateWorkerEvent));
  120. return E_FAIL;
  121. }
  122. H[0] = THandle32ToHandle(FEvents->TerminateWorkerEvent);
  123. H[1] = THandle32ToHandle(ProcessData.ParentProcess);
  124. H[2] = AWaitEvent;
  125. switch (WaitForMultipleObjects(3, H, FALSE, INFINITE)) {
  126. case WAIT_OBJECT_0 + 0:
  127. case WAIT_OBJECT_0 + 1:
  128. return E_ABORT;
  129. case WAIT_OBJECT_0 + 2:
  130. return S_OK;
  131. default:
  132. SetEvent(THandle32ToHandle(FEvents->TerminateWorkerEvent));
  133. return E_FAIL;
  134. }
  135. }
  136. static HRESULT CheckTerminateWorkerEvent(void)
  137. {
  138. HANDLE H[2];
  139. H[0] = THandle32ToHandle(FEvents->TerminateWorkerEvent);
  140. H[1] = THandle32ToHandle(ProcessData.ParentProcess);
  141. switch (WaitForMultipleObjects(2, H, FALSE, 0)) {
  142. case WAIT_OBJECT_0 + 0:
  143. case WAIT_OBJECT_0 + 1:
  144. return E_ABORT;
  145. case WAIT_TIMEOUT:
  146. return S_OK;
  147. default:
  148. SetEvent(THandle32ToHandle(FEvents->TerminateWorkerEvent));
  149. return E_FAIL;
  150. }
  151. }
  152. static HRESULT FillBuffer(const BOOL AWrite, void *Data, size_t Size,
  153. size_t *ProcessedSize)
  154. /* Called from worker thread (or a thread spawned by the worker thread) */
  155. {
  156. Byte *P;
  157. Longint Bytes;
  158. HRESULT Result;
  159. *ProcessedSize = 0;
  160. P = Data;
  161. while (Size != 0) {
  162. Longint LimitedSize = Size > MAXLONG ? MAXLONG : (Longint)Size;
  163. if (AWrite) {
  164. Bytes = RingBufferWrite(&FShared->OutputBuffer, P, LimitedSize);
  165. } else {
  166. if (FShared->NoMoreInput) {
  167. /* If NoMoreInput=True and *then* we see that the input buffer is
  168. empty (ordering matters!), we know that all input has been
  169. processed and that the input buffer will stay empty */
  170. MemoryBarrier();
  171. if (FShared->InputBuffer.Count == 0) {
  172. break;
  173. }
  174. }
  175. Bytes = RingBufferRead(&FShared->InputBuffer, P, LimitedSize);
  176. }
  177. if (Bytes == 0) {
  178. if (AWrite) {
  179. /* Output buffer full; wait for the main thread to flush it */
  180. Result = WakeMainAndWaitUntil(
  181. THandle32ToHandle(FEvents->WorkerWaitingOnOutputEvent),
  182. THandle32ToHandle(FEvents->EndWaitOnOutputEvent));
  183. if (Result != S_OK) {
  184. return Result;
  185. }
  186. } else {
  187. /* Input buffer empty; wait for the main thread to fill it */
  188. Result = WakeMainAndWaitUntil(
  189. THandle32ToHandle(FEvents->WorkerWaitingOnInputEvent),
  190. THandle32ToHandle(FEvents->EndWaitOnInputEvent));
  191. if (Result != S_OK) {
  192. return Result;
  193. }
  194. }
  195. } else {
  196. *ProcessedSize += Bytes;
  197. Size -= Bytes;
  198. P += Bytes;
  199. }
  200. }
  201. return S_OK;
  202. }
  203. static HRESULT Read(void *Data, size_t Size, size_t *ProcessedSize)
  204. /* Called from worker thread (or a thread spawned by the worker thread) */
  205. {
  206. HRESULT Result;
  207. /* Sanity check: Make sure we're the only thread inside Read */
  208. if (InterlockedExchange(&FReadLock, 1) != 0) {
  209. return E_FAIL;
  210. }
  211. Result = FillBuffer(FALSE, Data, Size, ProcessedSize);
  212. InterlockedExchange(&FReadLock, 0);
  213. return Result;
  214. }
  215. static HRESULT Write(const void *Data, size_t Size, size_t *ProcessedSize)
  216. /* Called from worker thread (or a thread spawned by the worker thread) */
  217. {
  218. HRESULT Result;
  219. /* Sanity check: Make sure we're the only thread inside Write */
  220. if (InterlockedExchange(&FWriteLock, 1) != 0) {
  221. return E_FAIL;
  222. }
  223. Result = FillBuffer(TRUE, (void *)Data, Size, ProcessedSize);
  224. InterlockedExchange(&FWriteLock, 0);
  225. return Result;
  226. }
  227. static HRESULT ProgressMade(const UInt64 TotalBytesProcessed)
  228. /* Called from worker thread (or a thread spawned by the worker thread) */
  229. {
  230. HRESULT Result;
  231. /* Sanity check: Make sure we're the only thread inside Progress */
  232. if (InterlockedExchange(&FProgressLock, 1) != 0) {
  233. return E_FAIL;
  234. }
  235. /* An Interlocked function is used to ensure the 64-bit value is written
  236. atomically (not with two separate 32-bit writes).
  237. TLZMACompressor will ignore negative values. LZMA SDK's 7zTypes.h says
  238. "-1 for size means unknown value", though I don't see any place
  239. where LzmaEnc actually does call Progress with inSize = -1. */
  240. InterlockedExchange64(&FShared->ProgressBytes, (Int64)TotalBytesProcessed);
  241. Result = CheckTerminateWorkerEvent();
  242. InterlockedExchange(&FProgressLock, 0);
  243. return Result;
  244. }
  245. static SRes LZMASeqInStreamReadWrapper(ISeqInStreamPtr p, void *buf, size_t *size)
  246. {
  247. if (Read(buf, *size, size) == S_OK) {
  248. return SZ_OK;
  249. } else {
  250. return SZ_ERROR_READ;
  251. }
  252. }
  253. static size_t LZMASeqOutStreamWriteWrapper(ISeqOutStreamPtr p, const void *buf, size_t size)
  254. {
  255. size_t Result;
  256. if (Write(buf, size, &Result) != S_OK) {
  257. return 0;
  258. }
  259. return Result;
  260. }
  261. static SRes LZMACompressProgressProgressWrapper(ICompressProgressPtr p, UInt64 inSize, UInt64 outSize)
  262. {
  263. if (ProgressMade(inSize) == S_OK) {
  264. return SZ_OK;
  265. } else {
  266. return SZ_ERROR_PROGRESS;
  267. }
  268. }
  269. static int BeginEncode(void)
  270. {
  271. static ISeqInStream InStream = { LZMASeqInStreamReadWrapper };
  272. static ISeqOutStream OutStream = { LZMASeqOutStreamWriteWrapper };
  273. static ICompressProgress CompressProgress = { LZMACompressProgressProgressWrapper };
  274. struct LZMAHandle *FLZMAHandle;
  275. SRes res;
  276. HANDLE H[3];
  277. res = LZMA_Init(ProcessData.LZMA2, &FLZMAHandle);
  278. if (res != S_OK) {
  279. return MAKELONG(res, 20);
  280. }
  281. res = LZMA_SetProps(FLZMAHandle, &ProcessData.EncoderProps,
  282. sizeof(ProcessData.EncoderProps));
  283. if (res != S_OK) {
  284. return MAKELONG(res, 21);
  285. }
  286. // WorkerThreadProc:
  287. H[0] = THandle32ToHandle(FEvents->TerminateWorkerEvent);
  288. H[1] = THandle32ToHandle(ProcessData.ParentProcess);
  289. H[2] = THandle32ToHandle(FEvents->StartEncodeEvent);
  290. while (WaitForMultipleObjects(3, H, FALSE, INFINITE) == WAIT_OBJECT_0 + 2) {
  291. FShared->EncodeResult = LZMA_Encode(FLZMAHandle, &InStream, &OutStream,
  292. &CompressProgress);
  293. if (!SetEvent(THandle32ToHandle(FEvents->WorkerEncodeFinishedEvent))) {
  294. break;
  295. }
  296. }
  297. res = LZMA_End(FLZMAHandle);
  298. return MAKELONG(res, 22);
  299. }
  300. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  301. LPWSTR lpCmdLine, int nCmdShow)
  302. {
  303. int Version;
  304. THandle32 ProcessDataMapping;
  305. struct TLZMACompressorProcessData *ProcessDataView;
  306. if (__argc < 3) {
  307. MessageBox(NULL, TEXT("This program is used internally by ")
  308. TEXT("the Inno Setup Compiler for LZMA compression. ")
  309. TEXT("It cannot be started directly."),
  310. TEXT("LZMA Compression Helper"), MB_OK | MB_ICONINFORMATION);
  311. return MAKELONG(0, 1);
  312. }
  313. if (!StrToIntEx(__wargv[1], STIF_DEFAULT, &Version) ||
  314. Version != ISLZMA_EXE_VERSION) {
  315. return MAKELONG(0, 2);
  316. }
  317. if (!StrToIntEx(__wargv[2], STIF_SUPPORT_HEX, &ProcessDataMapping)) {
  318. return MAKELONG(0, 3);
  319. }
  320. ProcessDataView = MapViewOfFile(THandle32ToHandle(ProcessDataMapping),
  321. FILE_MAP_READ, 0, 0, sizeof(ProcessData));
  322. if (!ProcessDataView) {
  323. return MAKELONG(GetLastError(), 4);
  324. }
  325. ProcessData = *ProcessDataView;
  326. UnmapViewOfFile(ProcessDataView);
  327. CloseHandle(THandle32ToHandle(ProcessDataMapping));
  328. if (ProcessData.StructSize != sizeof(ProcessData)) {
  329. return MAKELONG(0, 5);
  330. }
  331. if (ProcessData.SharedDataStructSize != sizeof(*FShared)) {
  332. return MAKELONG(0, 6);
  333. }
  334. FEvents = &ProcessData.Events;
  335. FShared = MapViewOfFile(THandle32ToHandle(ProcessData.SharedDataMapping),
  336. FILE_MAP_WRITE, 0, 0, sizeof(*FShared));
  337. if (!FShared) {
  338. return MAKELONG(GetLastError(), 7);
  339. }
  340. return BeginEncode();
  341. }