1 /++
2 	Provides LZMA (aka .xz), gzip (.gz) and .tar file read-only support.
3 	Combine to read .tar.xz and .tar.gz files, or use in conjunction with
4 	other libraries to read other kinds of files.
5 
6 	Also has a custom archive called arcz read and write support.
7 	It is designed to efficiently pack and randomly access large
8 	numbers of similar files. Unlike .zip files, it will do
9 	cross-file compression (meaning it can significantly shrink
10 	archives with several small but similar files), and unlike
11 	tar.gz files, it supports random access without decompressing
12 	the whole archive to get an individual file. It is designed
13 	for large numbers of small, similar files.
14 
15 	History:
16 		tar code (and arsd module formed) originally written December 2019 to support my d_android library downloader. It was added to dub in March 2020 (dub v7.0).
17 
18 		The LZMA code is a D port of Igor Pavlov's LzmaDec.h, written in 2017 with contributions by Lasse Collin. It was ported to D by ketmar some time after that and included in the original version of `arsd.archive` in the first December 2019 release.
19 
20 		The arcz code was written by ketmar in 2016 and added to arsd.archive in March 2020.
21 
22 		A number of improvements were made with the help of Steven Schveighoffer on March 22, 2023.
23 
24 		`arsd.archive` was changed to require [arsd.core] on March 23, 2023 (dub v11.0). Previously, it was a standalone module. It uses arsd.core's exception helpers only at this time and you could turn them back into plain (though uninformative) D base `Exception` instances to remove the dependency if you wanted to keep the file independent.
25 
26                 The [ArzArchive] class had a memory leak prior to November 2, 2024. It now uses the GC instead.
27 +/
28 module arsd.archive;
29 
30 import arsd.core;
31 
32 version(WithoutLzmaDecoder) {} else
33 version=WithLzmaDecoder;
34 
35 version(WithoutArczCode) {} else
36 version=WithArczCode;
37 
38 /+
39 /++
40 	Reads a tar file and passes the chunks to your handler. Use it like:
41 
42 	TarFile f = TarFile("filename.tar");
43 	foreach(part; f) {
44 		if(part.isNewFile) {
45 
46 		}
47 	}
48 
49 	FIXME not implemented
50 +/
51 struct TarFile {
52 	this(string filename) {
53 
54 	}
55 }
56 +/
57 
58 inout(char)[] upToZero(inout(char)[] a) {
59 	int i = 0;
60 	while(i < a.length && a[i]) i++;
61 	return a[0 .. i];
62 }
63 
64 
65 /++
66 	A header of a file in the archive. This represents the
67 	binary format of the header block.
68 +/
69 align(512)
70 struct TarFileHeader {
71 	align(1):
72 	char[100] fileName_ = 0;
73 	char[8] fileMode_ = 0;
74 	char[8] ownerUid_ = 0;
75 	char[8] ownerGid_ = 0;
76 	char[12] size_ = 0; // in octal
77 	char[12] mtime_ = 0; // octal unix timestamp
78 	char[8] checksum_ = 0; // right?????
79 	char[1] fileType_ = 0; // hard link, soft link, etc
80 	char[100] linkFileName_ = 0;
81 	char[6] ustarMagic_ = 0; // if "ustar\0", remaining fields are set
82 	char[2] ustarVersion_ = 0;
83 	char[32] ownerName_ = 0;
84 	char[32] groupName_ = 0;
85 	char[8] deviceMajorNumber_ = 0;
86 	char[8] deviceMinorNumber_ = 0;
87 	char[155] filenamePrefix_ = 0;
88 
89 	/// Returns the filename. You should cache the return value as long as TarFileHeader is in scope (it returns a slice after calling strlen)
90 	const(char)[] filename() {
91 		import core.stdc.string;
92 		if(filenamePrefix_[0])
93 			return upToZero(filenamePrefix_[]) ~ upToZero(fileName_[]);
94 		return upToZero(fileName_[]);
95 	}
96 
97 	/++
98 		Returns the target of a symlink or hardlink. Remember, this returns a slice of the TarFileHeader structure, so once it goes out of scope, this slice will be dangling!
99 
100 		History:
101 			Added March 24, 2023 (dub v11.0)
102 	+/
103 	const(char)[] linkFileName() {
104 		return upToZero(linkFileName_[]);
105 	}
106 
107 	///
108 	ulong size() {
109 		import core.stdc.stdlib;
110 		return strtoul(size_.ptr, null, 8);
111 	}
112 
113 	///
114 	TarFileType type() {
115 		if(fileType_[0] == 0)
116 			return TarFileType.normal;
117 		else
118 			return cast(TarFileType) (fileType_[0] - '0');
119 	}
120 
121 	///
122 	uint mode() {
123 		import core.stdc.stdlib;
124 		return cast(uint) strtoul(fileMode_.ptr, null, 8);
125 	}
126 }
127 
128 /// There's other types but this is all I care about. You can still detect the char by `((cast(char) type) + '0')`
129 enum TarFileType {
130 	normal = 0, ///
131 	hardLink = 1, ///
132 	symLink = 2, ///
133 	characterSpecial = 3, ///
134 	blockSpecial = 4, ///
135 	directory = 5, ///
136 	fifo = 6 ///
137 }
138 
139 
140 
141 
142 /++
143 	Low level tar file processor. You must pass it a
144 	TarFileHeader buffer as well as a size_t for context.
145 	Both must be initialized to all zeroes on first call,
146 	then not modified in between calls.
147 
148 	Each call must populate the dataBuffer with 512 bytes.
149 
150 	returns true if still work to do.
151 
152 	Please note that it currently only passes regular files, hard and soft links, and directories to your handler.
153 
154 	History:
155 		[TarFileType.symLink] and [TarFileType.hardLink] used to be skipped by this function. On March 24, 2023, it was changed to send them to your `handleData` delegate too. The `data` argument to your handler will have the target of the link. Check `header.type` to know if it is a hard link, symbolic link, directory, normal file, or other special type (which are still currently skipped, but future proof yourself by either skipping or handling them now).
156 +/
157 bool processTar(
158 	TarFileHeader* header,
159 	long* bytesRemainingOnCurrentFile,
160 	const(ubyte)[] dataBuffer,
161 	scope void delegate(TarFileHeader* header, bool isNewFile, bool fileFinished, const(ubyte)[] data) handleData
162 )
163 {
164 	assert(dataBuffer.length == 512);
165 	assert(bytesRemainingOnCurrentFile !is null);
166 	assert(header !is null);
167 
168 	if(*bytesRemainingOnCurrentFile) {
169 		bool isNew = *bytesRemainingOnCurrentFile == header.size();
170 		if(*bytesRemainingOnCurrentFile <= 512) {
171 			handleData(header, isNew, true, dataBuffer[0 .. cast(size_t) *bytesRemainingOnCurrentFile]);
172 			*bytesRemainingOnCurrentFile = 0;
173 		} else {
174 			handleData(header, isNew, false, dataBuffer[]);
175 			*bytesRemainingOnCurrentFile -= 512;
176 		}
177 	} else {
178 		*header = *(cast(TarFileHeader*) dataBuffer.ptr);
179 		auto s = header.size();
180 		*bytesRemainingOnCurrentFile = s;
181 		if(header.type() == TarFileType.directory)
182 			handleData(header, true, false, null);
183 		if(header.type() == TarFileType.hardLink || header.type() == TarFileType.symLink)
184 			handleData(header, true, true, cast(ubyte[]) header.linkFileName());
185 		if(s == 0 && header.type == TarFileType.normal)
186 			return false;
187 	}
188 
189 	return true;
190 }
191 
192 ///
193 unittest {
194 /+
195 	void main() {
196 		TarFileHeader tfh;
197 		long size;
198 
199 		import std.stdio;
200 		ubyte[512] buffer;
201 		foreach(chunk; File("/home/me/test/pl.tar", "r").byChunk(buffer[])) {
202 			processTar(&tfh, &size, buffer[],
203 			(header, isNewFile, fileFinished, data) {
204 				if(isNewFile)
205 					writeln("**** " , header.filename, " ", header.size);
206 				write(cast(string) data);
207 				if(fileFinished)
208 					writeln("+++++++++++++++");
209 
210 			});
211 		}
212 	}
213 
214 	main();
215 +/
216 }
217 
218 
219 // Advances data up to the end of the vla
220 ulong readVla(ref const(ubyte)[] data) {
221 	ulong n = 0;
222 	int i = 0;
223 
224 	while (data[0] & 0x80) {
225 		ubyte b = data[0];
226 		data = data[1 .. $];
227 
228 		assert(b != 0);
229 		if(b == 0) return 0;
230 
231 		n |= cast(ulong)(b & 0x7F) << (i * 7);
232 		i++;
233 	}
234 	ubyte b = data[0];
235 	data = data[1 .. $];
236 	n |= cast(ulong)(b & 0x7F) << (i * 7);
237 
238 	return n;
239 }
240 
241 /++
242 	decompressLzma lzma (.xz file) decoder/decompressor that works by passed functions. Can be used as a higher-level alternative to [XzDecoder]. decompressGzip is  gzip (.gz file) decoder/decompresser) that works by passed functions. Can be used as an alternative to [std.zip], while using the same underlying zlib library.
243 
244 	Params:
245 		chunkReceiver = a function that receives chunks of uncompressed data and processes them. Note that the chunk you receive will be overwritten once your function returns, so make sure you write it to a file or copy it to an outside array if you want to keep the data
246 
247 		bufferFiller = a function that fills the provided buffer as much as you can, then returns the slice of the buffer you actually filled.
248 
249 		chunkBuffer = an optional parameter providing memory that will be used to buffer uncompressed data chunks. If you pass `null`, it will allocate one for you. Any data in the buffer will be immediately overwritten.
250 
251 		inputBuffer = an optional parameter providing memory that will hold compressed input data. If you pass `null`, it will allocate one for you. You should NOT populate this buffer with any data; it will be immediately overwritten upon calling this function. The `inputBuffer` must be at least 64 bytes in size.
252 
253 		allowPartialChunks = can be set to true if you want `chunkReceiver` to be called as soon as possible, even if it is only partially full before the end of the input stream. The default is to fill the input buffer for every call to `chunkReceiver` except the last which has remainder data from the input stream.
254 
255 	History:
256 		Added March 24, 2023 (dub v11.0)
257 
258 		On October 25, 2024, the implementation got a major fix - it can read multiple blocks off the xz file now, were as before it would stop at the first one. This changed the requirement of the input buffer minimum size from 32 to 64 bytes (but it is always better to go more, I recommend 32 KB).
259 +/
260 version(WithLzmaDecoder)
261 void decompressLzma(scope void delegate(in ubyte[] chunk) chunkReceiver, scope ubyte[] delegate(ubyte[] buffer) bufferFiller, ubyte[] chunkBuffer = null, ubyte[] inputBuffer = null, bool allowPartialChunks = false) @trusted {
262 	if(chunkBuffer is null)
263 		chunkBuffer = new ubyte[](1024 * 32);
264 	if(inputBuffer is null)
265 		inputBuffer = new ubyte[](1024 * 32);
266 
267 	assert(inputBuffer.length >= 64);
268 
269 	bool isStartOfFile = true;
270 
271 	const(ubyte)[] compressedData = bufferFiller(inputBuffer[]);
272 
273 	XzDecoder decoder = XzDecoder(compressedData);
274 
275 	compressedData = decoder.unprocessed;
276 
277 	auto usableChunkBuffer = chunkBuffer;
278 
279 	while(!decoder.finished) {
280 		auto newChunk = decoder.processData(usableChunkBuffer, compressedData);
281 
282 		auto chunk = chunkBuffer[0 .. (newChunk.ptr - chunkBuffer.ptr) + newChunk.length];
283 
284 		if(chunk.length && (decoder.finished || allowPartialChunks || chunk.length == chunkBuffer.length)) {
285 			chunkReceiver(chunk);
286 			usableChunkBuffer = chunkBuffer;
287 		} else if(!decoder.finished) {
288 			// if we're here we got a partial chunk
289 			usableChunkBuffer = chunkBuffer[chunk.length .. $];
290 		}
291 
292 		if(decoder.needsMoreData) {
293 			import core.stdc.string;
294 			memmove(inputBuffer.ptr, decoder.unprocessed.ptr, decoder.unprocessed.length);
295 
296 			auto newlyRead = bufferFiller(inputBuffer[decoder.unprocessed.length .. $]);
297 			assert(newlyRead.ptr >= inputBuffer.ptr && newlyRead.ptr < inputBuffer.ptr + inputBuffer.length);
298 
299 			compressedData = inputBuffer[0 .. decoder.unprocessed.length + newlyRead.length];
300 		} else {
301 			compressedData = decoder.unprocessed;
302 		}
303 	}
304 }
305 
306 /// ditto
307 void decompressGzip(scope void delegate(in ubyte[] chunk) chunkReceiver, scope ubyte[] delegate(ubyte[] buffer) bufferFiller, ubyte[] chunkBuffer = null, ubyte[] inputBuffer = null, bool allowPartialChunks = false) @trusted {
308 
309 	import etc.c.zlib;
310 
311 	if(chunkBuffer is null)
312 		chunkBuffer = new ubyte[](1024 * 32);
313 	if(inputBuffer is null)
314 		inputBuffer = new ubyte[](1024 * 32);
315 
316 	const(ubyte)[] compressedData = bufferFiller(inputBuffer[]);
317 
318 	z_stream zs;
319 
320 	scope(exit)
321 		inflateEnd(&zs); // can return Z_STREAM_ERROR if state inconsistent
322 
323 	int windowBits = 15 + 32; // determine header from data
324 
325 	int err = inflateInit2(&zs, 15 + 32); // determine header from data
326 	if(err)
327 		throw ArsdException!"zlib"(err, zs.msg[0 .. 80].upToZero.idup); // FIXME: the 80 limit is arbitrary
328 	// zs.msg is also an error message string
329 
330 	zs.next_in = compressedData.ptr;
331 	zs.avail_in = cast(uint) compressedData.length;
332 
333 	while(true) {
334 		zs.next_out = chunkBuffer.ptr;
335 		zs.avail_out = cast(uint) chunkBuffer.length;
336 
337 		fill_more_chunk:
338 
339 		err = inflate(&zs, Z_NO_FLUSH);
340 
341 		if(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR) {
342 			import core.stdc.string;
343 
344 			auto decompressed = chunkBuffer[0 .. chunkBuffer.length - zs.avail_out];
345 
346 			// if the buffer is full, we always send a chunk.
347 			// partial chunks can be enabled, but we still will never send an empty chunk
348 			// if we're at the end of a stream, we always send the final chunk
349 			if(zs.avail_out == 0 || ((err == Z_STREAM_END || allowPartialChunks) && decompressed.length)) {
350 				chunkReceiver(decompressed);
351 			} else if(err != Z_STREAM_END) {
352 				// need more data to fill the next chunk
353 				if(zs.avail_in) {
354 					memmove(inputBuffer.ptr, zs.next_in, zs.avail_in);
355 				}
356 				auto newlyRead = bufferFiller(inputBuffer[zs.avail_in .. $ - zs.avail_in]);
357 
358 				assert(newlyRead.ptr >= inputBuffer.ptr && newlyRead.ptr < inputBuffer.ptr + inputBuffer.length);
359 
360 				zs.next_in = inputBuffer.ptr;
361 				zs.avail_in = cast(int) (zs.avail_in + newlyRead.length);
362 
363 				if(zs.avail_out)
364 					goto fill_more_chunk;
365 			} else {
366 				assert(0, "progress impossible; your input buffer of compressed data might be too small");
367 			}
368 
369 			if(err == Z_STREAM_END)
370 				break;
371 		} else {
372 			throw ArsdException!"zlib"(err, zs.msg[0 .. 80].upToZero.idup); // FIXME: the 80 limit is arbitrary
373 		}
374 	}
375 }
376 
377 
378 
379 /// [decompressLzma] and [processTar] can be used together like this:
380 unittest {
381 /+
382 	import arsd.archive;
383 
384 	void main() {
385 		import std.stdio;
386 		auto file = File("test.tar.xz");
387 
388 		TarFileHeader tfh;
389 		long size;
390 		ubyte[512] tarBuffer;
391 
392 		decompressLzma(
393 			(in ubyte[] chunk) => cast(void) processTar(&tfh, &size, chunk,
394 				(header, isNewFile, fileFinished, data) {
395 					if(isNewFile)
396 						writeln("**** " , header.filename, " ", header.size);
397 					//write(cast(string) data);
398 					if(fileFinished)
399 						writeln("+++++++++++++++");
400 				}),
401 			(ubyte[] buffer) => file.rawRead(buffer),
402 			tarBuffer[]
403 		);
404 	}
405 +/
406 }
407 
408 /++
409 	A simple .xz file decoder.
410 
411 	See the constructor and [processData] docs for details.
412 
413 	You might prefer using [decompressLzma] for a higher-level api.
414 
415 	FIXME: it doesn't implement very many checks, instead
416 	assuming things are what it expects. Don't use this without
417 	assertions enabled!
418 +/
419 version(WithLzmaDecoder)
420 struct XzDecoder {
421 	/++
422 		Start decoding by feeding it some initial data. You must
423 		send it at least enough bytes for the header (> 16 bytes prolly);
424 		try to send it a reasonably sized chunk.
425 
426 		It sets `this.unprocessed` to be a slice of the *tail* of the `initialData`
427 		member, indicating leftover data after parsing the header. You will need to
428 		pass this to [processData] at least once to start decoding the data left over
429 		after the header. See [processData] for more information.
430 	+/
431 	this(const(ubyte)[] initialData) {
432 
433 		ubyte[6] magic;
434 
435 		magic[] = initialData[0 .. magic.length];
436 		initialData = initialData[magic.length .. $];
437 
438 		if(cast(string) magic != "\xFD7zXZ\0")
439 			throw new Exception("not an xz file");
440 
441 		ubyte[2] streamFlags = initialData[0 .. 2];
442 		initialData = initialData[2 .. $];
443 
444 		// size of the check at the end in the footer. im just ignoring tbh
445 		checkSize = streamFlags[1] == 0 ? 0 : (4 << ((streamFlags[1]-1) / 3));
446 
447 		//uint crc32 = initialData[0 .. 4]; // FIXME just cast it. this is the crc of the flags.
448 		initialData = initialData[4 .. $];
449 
450 		state = State.readingHeader;
451 		readBlockHeader(initialData);
452 	}
453 
454 	private enum State {
455 		readingHeader,
456 		readingData,
457 		readingFooter,
458 	}
459 	private State state;
460 
461 	// returns true if it successfully read it, false if it needs more data
462 	private bool readBlockHeader(const(ubyte)[] initialData) {
463 		// now we are into an xz block...
464 
465 		if(initialData.length == 0) {
466 			unprocessed = initialData;
467 			needsMoreData_ = true;
468 			finished_ = false;
469 			return false;
470 		}
471 
472 		if(initialData[0] == 0) {
473 			// this is actually an index and a footer...
474 			// we could process it but this also really marks us being done!
475 
476 			// FIXME: should actually pull the data out and finish it off
477 			// see Index records etc at https://tukaani.org/xz/xz-file-format.txt
478 			unprocessed = null;
479 			finished_ = true;
480 			needsMoreData_ = false;
481 			return true;
482 		}
483 
484 		int blockHeaderSize = (initialData[0] + 1) * 4;
485 
486 		auto first = initialData.ptr;
487 
488 		if(blockHeaderSize > initialData.length) {
489 			unprocessed = initialData;
490 			needsMoreData_ = true;
491 			finished_ = false;
492 			return false;
493 		}
494 
495 		auto srcPostHeader = initialData[blockHeaderSize .. $];
496 
497 		initialData = initialData[1 .. $];
498 
499 		ubyte blockFlags = initialData[0];
500 		initialData = initialData[1 .. $];
501 
502 		if(blockFlags & 0x40) {
503 			compressedSize = readVla(initialData);
504 		} else {
505 			compressedSize = 0;
506 		}
507 
508 		if(blockFlags & 0x80) {
509 			uncompressedSize = readVla(initialData);
510 		} else {
511 			uncompressedSize = 0;
512 		}
513 
514 		//import std.stdio; writeln(compressedSize , " compressed, expands to ", uncompressedSize);
515 
516 		auto filterCount = (blockFlags & 0b11) + 1;
517 
518 		ubyte props;
519 
520 		foreach(f; 0 .. filterCount) {
521 			auto fid = readVla(initialData);
522 			auto sz = readVla(initialData);
523 
524 			// import std.stdio; writefln("%02x %d", fid, sz);
525 			assert(fid == 0x21);
526 			assert(sz == 1);
527 
528 			props = initialData[0];
529 			initialData = initialData[1 .. $];
530 		}
531 
532 		// writeln(initialData.ptr);
533 		// writeln(srcPostHeader.ptr);
534 
535 		// there should be some padding to a multiple of 4...
536 		// three bytes of zeroes given the assumptions here
537 
538 		assert(blockHeaderSize >= 4);
539 		long expectedRemainder = cast(long) blockHeaderSize - 4;
540 		expectedRemainder -= initialData.ptr - first;
541 		assert(expectedRemainder >= 0);
542 
543 		while(expectedRemainder) {
544 			expectedRemainder--;
545 			if(initialData[0] != 0)
546 				throw new Exception("non-zero where padding byte expected in xz file");
547 			initialData = initialData[1 .. $];
548 		}
549 
550 		// and then a header crc
551 
552 		initialData = initialData[4 .. $]; // skip header crc
553 
554 		assert(initialData.ptr is srcPostHeader.ptr);
555 
556 		// skip unknown header bytes
557 		while(initialData.ptr < srcPostHeader.ptr) {
558 			initialData = initialData[1 .. $];
559 		}
560 
561 		// should finally be at compressed data...
562 
563 		//writeln(compressedSize);
564 		//writeln(uncompressedSize);
565 
566 		if(Lzma2Dec_Allocate(&lzmaDecoder, props) != SRes.OK) {
567 			assert(0);
568 		}
569 
570 		Lzma2Dec_Init(&lzmaDecoder);
571 
572 		unprocessed = initialData;
573 		state = State.readingData;
574 
575 		return true;
576 	}
577 
578 	private bool readBlockFooter(const(ubyte)[] data) {
579 		// skip block padding
580 		while(data.length && data[0] == 0) {
581 			data = data[1 .. $];
582 		}
583 
584 		if(data.length < checkSize) {
585 			unprocessed = data;
586 			finished_ = false;
587 			needsMoreData_ = true;
588 			return false;
589 		}
590 
591 		// skip the check
592 		data = data[checkSize .. $];
593 
594 		state = State.readingHeader;
595 
596 		return readBlockHeader(data);
597 		//return true;
598 	}
599 
600 	~this() {
601 		LzmaDec_FreeProbs(&lzmaDecoder.decoder);
602 	}
603 
604 	/++
605 		Continues an in-progress decompression of the `src` data, putting it into the `dest` buffer.
606 		The `src` data must be at least 20 bytes long, but I'd recommend making it at larger.
607 
608 		Returns the slice of the head of the `dest` buffer actually filled, then updates the following
609 		member variables of `XzDecoder`:
610 
611 		$(LIST
612 			* [finished] will be `true` if the compressed data has been completely decompressed.
613 			* [needsMoreData] will be `true` if more src data was needed to fill the `dest` buffer. Note that you can also check this from the return value; if the return value's length is less than the destination buffer's length and the decoder is not finished, that means you needed more data to fill it.
614 			* And very importantly, [unprocessed] will contain a slice of the $(I tail of the `src` buffer) holding thus-far unprocessed data from the source buffer. This will almost always be set unless `dest` was big enough to hold the entire remaining uncompressed data.
615 		)
616 
617 		This function will not modify the `src` buffer in any way. This is why the [unprocessed] member holds a slice to its tail - it is your own responsibility to decide how to proceed.
618 
619 		If your `src` buffer contains the entire compressed file, you can pass `unprocessed` in a loop until finished:
620 
621 		---
622 		static import std.file;
623 		auto compressedData = cast(immutable(ubyte)[]) std.file.read("file.xz"); // load it all into memory at once
624 		XzDecoder decoder = XzDecoder(compressedData);
625 		auto buffer = new ubyte[](4096); // to hold chunks of uncompressed data
626 		ubyte[] wholeUncompressedFile;
627 		while(!decoder.finished) {
628 			// it returns the slice of buffer with new data, so we can append that
629 			// to reconstruct the whole file. and then it sets `decoded.unprocessed` to
630 			// a slice of what is left out of the source file to continue processing in
631 			// the next iteration of the loop.
632 			wholeUncompressedFile ~= decoder.processData(buffer, decoder.unprocessed);
633 		}
634 		// wholeUncompressedFile is now fully populated
635 		---
636 
637 		If you are reading from a file or some other streaming source, you may need to either move the unprocessed data back to the beginning of the buffer then load more into it, or copy it to a new, larger buffer and append more data then.
638 
639 		---
640 		import std.stdio;
641 		auto file = File("file.xz");
642 		ubyte[] compressedDataBuffer = new ubyte[](1024 * 32);
643 
644 		// read the first chunk. all modifications will be done through `compressedDataBuffer`
645 		// so we can make this part const for easier assignment to the slices `decoder.unprocessed` will hold
646 		const(ubyte)[] compressedData = file.rawRead(compressedDataBuffer);
647 
648 		XzDecoder decoder = XzDecoder(compressedData);
649 
650 		// need to keep what was unprocessed after construction
651 		compressedData = decoder.unprocessed;
652 
653 		auto buffer = new ubyte[](4096); // to hold chunks of uncompressed data
654 		ubyte[] wholeUncompressedFile;
655 		while(!decoder.finished) {
656 			wholeUncompressedFile ~= decoder.processData(buffer, compressedData);
657 
658 			if(decoder.needsMoreData) {
659 				// it needed more data to fill the buffer
660 
661 				// first, move the unprocessed data bask to the head
662 				// you cannot necessarily use a D slice assign operator
663 				// because the `unprocessed` is a slice of the `compressedDataBuffer`,
664 				// meaning they might overlap. Instead, we'll use C's `memmove`
665 				import core.stdc.string;
666 				memmove(compressedDataBuffer.ptr, decoder.unprocessed.ptr, decoder.unprocessed.length);
667 
668 
669 				// now we can read more data to fill in the tail of the buffer again
670 				auto newlyRead = file.rawRead(compressedDataBuffer[decoder.unprocessed.length .. $]);
671 
672 				// the new compressed data ready to process is what we moved from before,
673 				// now at the head of the buffer, plus what was just read, at the end of
674 				// the same buffer
675 				compressedData = compressedDataBuffer[0 .. decoder.unprocessed.length + newlyRead.length];
676 			} else {
677 				// otherwise, the output buffer was full, but there's probably
678 				// still more unprocessed data. Set it to be used on the next
679 				// loop iteration.
680 				compressedData = decoder.unprocessed;
681 			}
682 		}
683 		// wholeUncompressedFile is now fully populated
684 		---
685 
686 
687 	+/
688 	ubyte[] processData(ubyte[] dest, const(ubyte)[] src) {
689 		if(state == State.readingHeader) {
690 			if(!readBlockHeader(src))
691 				return dest[0 .. 0];
692 			src = unprocessed;
693 		}
694 
695 		size_t destLen = dest.length;
696 		size_t srcLen = src.length;
697 
698 		ELzmaStatus status;
699 
700 		auto res = Lzma2Dec_DecodeToBuf(
701 			&lzmaDecoder,
702 			dest.ptr,
703 			&destLen,
704 			src.ptr,
705 			&srcLen,
706 			LZMA_FINISH_ANY,
707 			&status
708 		);
709 
710 		if(res != 0) {
711 			throw ArsdException!"Lzma2Dec_DecodeToBuf"(res);
712 		}
713 
714 		/+
715 		import std.stdio;
716 		writeln(res, " ", status);
717 		writeln(srcLen);
718 		writeln(destLen, ": ",  cast(string) dest[0 .. destLen]);
719 		+/
720 
721 		if(status == LZMA_STATUS_NEEDS_MORE_INPUT) {
722 			unprocessed = src[srcLen .. $];
723 			finished_ = false;
724 			needsMoreData_ = true;
725 		} else if(status == LZMA_STATUS_FINISHED_WITH_MARK || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) {
726 			// this is the end of a block, but not necessarily the end of the file
727 			state = State.readingFooter;
728 
729 			// the readBlockFooter function updates state, unprocessed, finished, and needs more data
730 			readBlockFooter(src[srcLen .. $]);
731 		} else if(status == LZMA_STATUS_NOT_FINISHED) {
732 			unprocessed = src[srcLen .. $];
733 			finished_ = false;
734 			needsMoreData_ = false;
735 		} else {
736 			// wtf
737 			throw ArsdException!"Unhandled LZMA_STATUS"(status);
738 		}
739 
740 		return dest[0 .. destLen];
741 	}
742 
743 	/++
744 		Returns true after [processData] has finished decoding the compressed data.
745 	+/
746 	bool finished() {
747 		return finished_;
748 	}
749 
750 	/++
751 		After calling [processData], this will return `true` if more data is required to fill
752 		the destination buffer.
753 
754 		Please note that `needsMoreData` can return `false` before decompression is completely
755 		[finished]; this would simply mean it satisfied the request to fill that one buffer.
756 
757 		In this case, you will want to concatenate [unprocessed] with new data, then call [processData]
758 		again. Remember that [unprocessed] is a slice of the tail of the source buffer you passed to
759 		`processData`, so if you want to reuse the same buffer, you may want to `memmove` it to the
760 		head, then fill he tail again.
761 	+/
762 	bool needsMoreData() {
763 		return needsMoreData_;
764 	}
765 
766 	private bool finished_;
767 	private bool needsMoreData_;
768 
769 	CLzma2Dec lzmaDecoder;
770 	int checkSize;
771 
772 	ulong compressedSize; ///
773 	ulong uncompressedSize; ///
774 
775 	const(ubyte)[] unprocessed; ///
776 }
777 
778 ///
779 /+
780 version(WithLzmaDecoder)
781 unittest {
782 
783 	void main() {
784 		ubyte[512] dest; // into tar size chunks!
785 		ubyte[1024] src;
786 
787 		import std.stdio;
788 
789 		//auto file = File("/home/me/test/amazing.txt.xz", "rb");
790 		auto file = File("/home/me/Android/ldcdl/test.tar.xz", "rb");
791 		auto bfr = file.rawRead(src[]);
792 
793 		XzDecoder xzd = XzDecoder(bfr);
794 
795 		// not necessarily set, don't rely on them
796 		writeln(xzd.compressedSize, " / ", xzd.uncompressedSize);
797 
798 		// for tar
799 		TarFileHeader tfh;
800 		long size;
801 
802 		long sum = 0;
803 		while(!xzd.finished) {
804 			// as long as your are not finished, there is more work to do. But it doesn't
805 			// necessarily need more data, so that is a separate check.
806 			if(xzd.needsMoreData) {
807 				// if it needs more data, append new stuff to the end of the buffer, after
808 				// the existing unprocessed stuff. If your buffer is too small, you may be
809 				// forced to grow it here, but anything >= 1 KB seems OK in my tests.
810 				bfr = file.rawRead(src[bfr.length - xzd.unprocessed.length .. $]);
811 			} else {
812 				// otherwise, you want to continue working with existing unprocessed data
813 				bfr = cast(ubyte[]) xzd.unprocessed;
814 			}
815 			//write(cast(string) xzd.processData(dest[], bfr));
816 
817 			auto buffer = xzd.processData(dest[], bfr);
818 
819 			// if the buffer is empty we are probably done
820 			// or need more data, so continue the loop to evaluate.
821 			if(buffer.length == 0)
822 				continue;
823 
824 			// our tar code requires specifically 512 byte pieces
825 			while(!xzd.finished && buffer.length != 512) {
826 				// need more data hopefully
827 				assert(xzd.needsMoreData);
828 				// using the existing buffer...
829 				bfr = file.rawRead(src[bfr.length - xzd.unprocessed.length .. $]);
830 				auto nbuffer = xzd.processData(dest[buffer.length .. $], bfr);
831 				buffer = dest[0 .. buffer.length + nbuffer.length];
832 			}
833 
834 			sum += buffer.length;
835 
836 			// process the buffer through the tar file handler
837 			processTar(&tfh, &size, buffer[],
838 			(header, isNewFile, fileFinished, data) {
839 				if(isNewFile)
840 					writeln("**** " , header.filename, " ", header.size);
841 				//write(cast(string) data);
842 				if(fileFinished)
843 					writeln("+++++++++++++++");
844 
845 			});
846 		}
847 
848 		writeln(sum);
849 	}
850 
851 	main();
852 }
853 +/
854 
855 version(WithArczCode) {
856 /* The code in this section was originally written by Ketmar Dark for his arcz.d module. I modified it afterward. */
857 
858 /** ARZ chunked archive format processor.
859  *
860  * This module provides `std.stdio.File`-like interface to ARZ archives.
861  *
862  * Copyright: Copyright Ketmar Dark, 2016
863  *
864  * License: Boost License 1.0
865  */
866 // module iv.arcz;
867 
868 // use Balz compressor if available
869 static if (__traits(compiles, { import iv.balz; })) enum arcz_has_balz = true; else enum arcz_has_balz = false;
870 static if (__traits(compiles, { import iv.zopfli; })) enum arcz_has_zopfli = true; else enum arcz_has_zopfli = false;
871 static if (arcz_has_balz) import iv.balz;
872 static if (arcz_has_zopfli) import iv.zopfli;
873 
874 // comment this to free pakced chunk buffer right after using
875 // i.e. `AZFile` will allocate new block for each new chunk
876 //version = arcz_use_more_memory;
877 
878 public import core.stdc.stdio : SEEK_SET, SEEK_CUR, SEEK_END;
879 
880 
881 // ////////////////////////////////////////////////////////////////////////// //
882 /// ARZ archive accessor. Use this to open ARZ archives, and open packed files from ARZ archives.
883 public struct ArzArchive {
884 private:
885   static assert(size_t.sizeof >= (void*).sizeof);
886   private import core.stdc.stdio : FILE, fopen, fclose, fread, fseek;
887   private import etc.c.zlib;
888 
889   static struct ChunkInfo {
890     uint ofs; // offset in file
891     uint pksize; // packed chunk size (same as chunk size: chunk is unpacked)
892   }
893 
894   static struct FileInfo {
895     string name;
896     uint chunk;
897     uint chunkofs; // offset of first file byte in unpacked chunk
898     uint size; // unpacked file size
899   }
900 
901   static struct Nfo {
902     uint rc = 1; // refcounter
903     ChunkInfo[] chunks;
904     FileInfo[string] files;
905     uint chunkSize;
906     uint lastChunkSize;
907     bool useBalz;
908     FILE* afl; // archive file, we'll keep it opened
909 
910     @disable this (this); // no copies!
911 
912     static void decRef (size_t me) {
913       if (me) {
914         auto nfo = cast(Nfo*)me;
915         assert(nfo.rc);
916         if (--nfo.rc == 0) {
917           import core.memory : GC;
918           // import core.stdc.stdlib : free;
919           if (nfo.afl !is null) fclose(nfo.afl);
920           nfo.chunks.destroy;
921           nfo.files.destroy;
922           nfo.afl = null;
923           GC.removeRange(cast(void*)nfo/*, Nfo.sizeof*/);
924           xfree(nfo);
925           debug(arcz_rc) { import core.stdc.stdio : printf; printf("Nfo %p freed\n", nfo); }
926         }
927       }
928     }
929   }
930 
931   size_t nfop; // hide it from GC
932 
933   private @property Nfo* nfo () { pragma(inline, true); return cast(Nfo*)nfop; }
934   void decRef () { pragma(inline, true); Nfo.decRef(nfop); nfop = 0; }
935 
936   static uint readUint (FILE* fl) {
937     if (fl is null) throw new Exception("cannot read from closed file");
938     uint v;
939     if (fread(&v, 1, v.sizeof, fl) != v.sizeof) throw new Exception("file reading error");
940     version(BigEndian) {
941       import core.bitop : bswap;
942       v = bswap(v);
943     } else version(LittleEndian) {
944       // nothing to do
945     } else {
946       static assert(0, "wtf?!");
947     }
948     return v;
949   }
950 
951   static uint readUbyte (FILE* fl) {
952     if (fl is null) throw new Exception("cannot read from closed file");
953     ubyte v;
954     if (fread(&v, 1, v.sizeof, fl) != v.sizeof) throw new Exception("file reading error");
955     return v;
956   }
957 
958   static void readBuf (FILE* fl, void[] buf) {
959     if (buf.length > 0) {
960       if (fl is null) throw new Exception("cannot read from closed file");
961       if (fread(buf.ptr, 1, buf.length, fl) != buf.length) throw new Exception("file reading error");
962     }
963   }
964 
965   static T* xalloc(T, bool clear=true) (uint mem) if (T.sizeof > 0) {
966     import core.memory;
967     import core.exception : onOutOfMemoryError;
968     assert(mem != 0);
969     static if (clear) {
970       // import core.stdc.stdlib : calloc;
971       // auto res = calloc(mem, T.sizeof);
972       auto res = GC.calloc(mem * T.sizeof, GC.BlkAttr.NO_SCAN);
973       if (res is null) onOutOfMemoryError();
974       static if (is(T == struct)) {
975         import core.stdc.string : memcpy;
976         static immutable T i = T.init;
977         foreach (immutable idx; 0..mem) memcpy(res+idx, &i, T.sizeof);
978       }
979       debug(arcz_alloc) { import core.stdc.stdio : printf; printf("allocated %u bytes at %p\n", cast(uint)(mem*T.sizeof), res); }
980       debug(arcz_alloc) { try { throw new Exception("mem trace c"); } catch(Exception e) { import std.stdio; writeln(e.toString()); } }
981       return cast(T*)res;
982     } else {
983       //import core.stdc.stdlib : malloc;
984       //auto res = malloc(mem*T.sizeof);
985       auto res = GC.malloc(mem*T.sizeof, GC.BlkAttr.NO_SCAN);
986       if (res is null) onOutOfMemoryError();
987       static if (is(T == struct)) {
988         import core.stdc.string : memcpy;
989         static immutable T i = T.init;
990         foreach (immutable idx; 0..mem) memcpy(res+idx, &i, T.sizeof);
991       }
992       debug(arcz_alloc) { import core.stdc.stdio : printf; printf("allocated %u bytes at %p\n", cast(uint)(mem*T.sizeof), res); }
993       debug(arcz_alloc) { try { throw new Exception("mem trace"); } catch(Exception e) { import std.stdio; writeln(e.toString()); } }
994       return cast(T*)res;
995     }
996   }
997 
998   static void xfree(T) (T* ptr) {
999     // just let the GC do it
1000     if(ptr !is null) {
1001         import core.memory;
1002         GC.free(ptr);
1003     }
1004 
1005 
1006     /+
1007     if (ptr !is null) {
1008       import core.stdc.stdlib : free;
1009       debug(arcz_alloc) { import core.stdc.stdio : printf; printf("freing at %p\n", ptr); }
1010       free(ptr);
1011     }
1012     +/
1013   }
1014 
1015   static if (arcz_has_balz) static ubyte balzDictSize (uint blockSize) {
1016     foreach (ubyte bits; Balz.MinDictBits..Balz.MaxDictBits+1) {
1017       if ((1U<<bits) >= blockSize) return bits;
1018     }
1019     return Balz.MaxDictBits;
1020   }
1021 
1022   // unpack exactly `destlen` bytes
1023   static if (arcz_has_balz) static void unpackBlockBalz (void* dest, uint destlen, const(void)* src, uint srclen, uint blocksize) {
1024     Unbalz bz;
1025     bz.reinit(balzDictSize(blocksize));
1026     int ipos, opos;
1027     auto dc = bz.decompress(
1028       // reader
1029       (buf) {
1030         import core.stdc.string : memcpy;
1031         if (ipos >= srclen) return 0;
1032         uint rd = destlen-ipos;
1033         if (rd > buf.length) rd = cast(uint)buf.length;
1034         memcpy(buf.ptr, src+ipos, rd);
1035         ipos += rd;
1036         return rd;
1037       },
1038       // writer
1039       (buf) {
1040         //if (opos+buf.length > destlen) throw new Exception("error unpacking archive");
1041         uint wr = destlen-opos;
1042         if (wr > buf.length) wr = cast(uint)buf.length;
1043         if (wr > 0) {
1044           import core.stdc.string : memcpy;
1045           memcpy(dest+opos, buf.ptr, wr);
1046           opos += wr;
1047         }
1048       },
1049       // unpack length
1050       destlen
1051     );
1052     if (opos != destlen) throw new Exception("error unpacking archive");
1053   }
1054 
1055   static void unpackBlockZLib (void* dest, uint destlen, const(void)* src, uint srclen, uint blocksize) {
1056     z_stream zs;
1057     zs.avail_in = 0;
1058     zs.avail_out = 0;
1059     // initialize unpacker
1060     if (inflateInit2(&zs, 15) != Z_OK) throw new Exception("can't initialize zlib");
1061     scope(exit) inflateEnd(&zs);
1062     zs.next_in = cast(typeof(zs.next_in))src;
1063     zs.avail_in = srclen;
1064     zs.next_out = cast(typeof(zs.next_out))dest;
1065     zs.avail_out = destlen;
1066     while (zs.avail_out > 0) {
1067       auto err = inflate(&zs, Z_SYNC_FLUSH);
1068       if (err != Z_STREAM_END && err != Z_OK) throw new Exception("error unpacking archive");
1069       if (err == Z_STREAM_END) break;
1070     }
1071     if (zs.avail_out != 0) throw new Exception("error unpacking archive");
1072   }
1073 
1074   static void unpackBlock (void* dest, uint destlen, const(void)* src, uint srclen, uint blocksize, bool useBalz) {
1075     if (useBalz) {
1076       static if (arcz_has_balz) {
1077         unpackBlockBalz(dest, destlen, src, srclen, blocksize);
1078       } else {
1079         throw new Exception("no Balz support was compiled in ArcZ");
1080       }
1081     } else {
1082       unpackBlockZLib(dest, destlen, src, srclen, blocksize);
1083     }
1084   }
1085 
1086 public:
1087   this (in ArzArchive arc) {
1088     assert(nfop == 0);
1089     nfop = arc.nfop;
1090     if (nfop) ++nfo.rc;
1091   }
1092 
1093   this (this) {
1094     if (nfop) ++nfo.rc;
1095   }
1096 
1097   ~this () { close(); }
1098 
1099   void opAssign (in ArzArchive arc) {
1100     if (arc.nfop) {
1101       auto n = cast(Nfo*)arc.nfop;
1102       ++n.rc;
1103     }
1104     decRef();
1105     nfop = arc.nfop;
1106   }
1107 
1108   void close () { decRef(); }
1109 
1110   @property FileInfo[string] files () { return (nfop ? nfo.files : null); }
1111 
1112   void openArchive (const(char)[] filename) {
1113     debug/*(arcz)*/ import core.stdc.stdio : printf;
1114     FILE* fl = null;
1115     scope(exit) if (fl !is null) fclose(fl);
1116     close();
1117     if (filename.length == 0) throw new Exception("cannot open unnamed archive file");
1118     if (false && filename.length < 2048) { // FIXME the alloca fails on win64 for some reason
1119       import core.stdc.stdlib : alloca;
1120       auto tfn = (cast(char*)alloca(filename.length+1))[0..filename.length+1];
1121       tfn[0..filename.length] = filename[];
1122       tfn[filename.length] = 0;
1123       fl = fopen(tfn.ptr, "rb");
1124     } else {
1125       import core.stdc.stdlib : malloc, free;
1126       auto tfn = (cast(char*)malloc(filename.length+1))[0..filename.length+1];
1127       if (tfn !is null) {
1128       	tfn[0 .. filename.length] = filename[];
1129 	tfn[filename.length] = 0;
1130         scope(exit) free(tfn.ptr);
1131         fl = fopen(tfn.ptr, "rb");
1132       }
1133     }
1134     if (fl is null) throw new Exception("cannot open archive file '"~filename.idup~"'");
1135     char[4] sign;
1136     bool useBalz;
1137     readBuf(fl, sign[]);
1138     if (sign != "CZA2") throw new Exception("invalid archive file '"~filename.idup~"'");
1139     switch (readUbyte(fl)) {
1140       case 0: useBalz = false; break;
1141       case 1: useBalz = true; break;
1142       default: throw new Exception("invalid version of archive file '"~filename.idup~"'");
1143     }
1144     uint indexofs = readUint(fl); // index offset in file
1145     uint pkidxsize = readUint(fl); // packed index size
1146     uint idxsize = readUint(fl); // unpacked index size
1147     if (pkidxsize == 0 || idxsize == 0 || indexofs == 0) throw new Exception("invalid archive file '"~filename.idup~"'");
1148     // now read index
1149     ubyte* idxbuf = null;
1150     scope(exit) xfree(idxbuf);
1151     {
1152       auto pib = xalloc!ubyte(pkidxsize);
1153       scope(exit) xfree(pib);
1154       if (fseek(fl, indexofs, 0) < 0) throw new Exception("seek error in archive file '"~filename.idup~"'");
1155       readBuf(fl, pib[0..pkidxsize]);
1156       idxbuf = xalloc!ubyte(idxsize);
1157       unpackBlock(idxbuf, idxsize, pib, pkidxsize, idxsize, useBalz);
1158     }
1159 
1160     // parse index and build structures
1161     uint idxbufpos = 0;
1162 
1163     ubyte getUbyte () {
1164       if (idxsize-idxbufpos < ubyte.sizeof) throw new Exception("invalid index for archive file '"~filename.idup~"'");
1165       return idxbuf[idxbufpos++];
1166     }
1167 
1168     uint getUint () {
1169       if (idxsize-idxbufpos < uint.sizeof) throw new Exception("invalid index for archive file '"~filename.idup~"'");
1170       version(BigEndian) {
1171         import core.bitop : bswap;
1172         uint v = *cast(uint*)(idxbuf+idxbufpos);
1173         idxbufpos += 4;
1174         return bswap(v);
1175       } else version(LittleEndian) {
1176         uint v = *cast(uint*)(idxbuf+idxbufpos);
1177         idxbufpos += 4;
1178         return v;
1179       } else {
1180         static assert(0, "wtf?!");
1181       }
1182     }
1183 
1184     void getBuf (void[] buf) {
1185       if (buf.length > 0) {
1186         import core.stdc.string : memcpy;
1187         if (idxsize-idxbufpos < buf.length) throw new Exception("invalid index for archive file '"~filename.idup~"'");
1188         memcpy(buf.ptr, idxbuf+idxbufpos, buf.length);
1189         idxbufpos += buf.length;
1190       }
1191     }
1192 
1193     // allocate shared info struct
1194     Nfo* nfo = xalloc!Nfo(1);
1195     assert(nfo.rc == 1);
1196     debug(arcz_rc) { import core.stdc.stdio : printf; printf("Nfo %p allocated\n", nfo); }
1197     scope(failure) decRef();
1198     nfop = cast(size_t)nfo;
1199     {
1200       import core.memory : GC;
1201       GC.addRange(nfo, Nfo.sizeof);
1202     }
1203 
1204     // read chunk info and data
1205     nfo.useBalz = useBalz;
1206     nfo.chunkSize = getUint;
1207     auto ccount = getUint; // chunk count
1208     nfo.lastChunkSize = getUint;
1209     debug(arcz_dirread) printf("chunk size: %u\nchunk count: %u\nlast chunk size:%u\n", nfo.chunkSize, ccount, nfo.lastChunkSize);
1210     if (ccount == 0 || nfo.chunkSize < 1 || nfo.lastChunkSize < 1 || nfo.lastChunkSize > nfo.chunkSize) throw new Exception("invalid archive file '"~filename.idup~"'");
1211     nfo.chunks.length = ccount;
1212     // chunk offsets and sizes
1213     foreach (ref ci; nfo.chunks) {
1214       ci.ofs = getUint;
1215       ci.pksize = getUint;
1216     }
1217     // read file count and info
1218     auto fcount = getUint;
1219     if (fcount == 0) throw new Exception("empty archive file '"~filename.idup~"'");
1220     // calc name buffer position and size
1221     //immutable uint nbofs = idxbufpos+fcount*(5*4);
1222     //if (nbofs >= idxsize) throw new Exception("invalid index in archive file '"~filename.idup~"'");
1223     //immutable uint nbsize = idxsize-nbofs;
1224     debug(arcz_dirread) printf("file count: %u\n", fcount);
1225     foreach (immutable _; 0..fcount) {
1226       uint nameofs = getUint;
1227       uint namelen = getUint;
1228       if (namelen == 0) {
1229         // skip unnamed file
1230         //throw new Exception("invalid archive file '"~filename.idup~"'");
1231         getUint; // chunk number
1232         getUint; // offset in chunk
1233         getUint; // unpacked size
1234         debug(arcz_dirread) printf("skipped empty file\n");
1235       } else {
1236         //if (nameofs >= nbsize || namelen > nbsize || nameofs+namelen > nbsize) throw new Exception("invalid index in archive file '"~filename.idup~"'");
1237         if (nameofs >= idxsize || namelen > idxsize || nameofs+namelen > idxsize) throw new Exception("invalid index in archive file '"~filename.idup~"'");
1238         FileInfo fi;
1239         auto nb = new char[](namelen);
1240         nb[0..namelen] = (cast(char*)idxbuf)[nameofs..nameofs+namelen];
1241         fi.name = cast(string)(nb); // it is safe here
1242         fi.chunk = getUint; // chunk number
1243         fi.chunkofs = getUint; // offset in chunk
1244         fi.size = getUint; // unpacked size
1245         debug(arcz_dirread) printf("file size: %u\nfile chunk: %u\noffset in chunk:%u; name: [%.*s]\n", fi.size, fi.chunk, fi.chunkofs, cast(uint)fi.name.length, fi.name.ptr);
1246         nfo.files[fi.name] = fi;
1247       }
1248     }
1249     // transfer achive file ownership
1250     nfo.afl = fl;
1251     fl = null;
1252   }
1253 
1254   bool exists (const(char)[] name) { if (nfop) return ((name in nfo.files) !is null); else return false; }
1255 
1256   AZFile open (const(char)[] name) {
1257     if (!nfop) throw new Exception("can't open file from non-opened archive");
1258     if (auto fi = name in nfo.files) {
1259       auto zl = xalloc!LowLevelPackedRO(1);
1260       scope(failure) xfree(zl);
1261       debug(arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p allocated\n", zl); }
1262       zl.setup(nfo, fi.chunk, fi.chunkofs, fi.size);
1263       AZFile fl;
1264       fl.zlp = cast(size_t)zl;
1265       return fl;
1266     }
1267     throw new Exception("can't open file '"~name.idup~"' from archive");
1268   }
1269 
1270 private:
1271   static struct LowLevelPackedRO {
1272     private import etc.c.zlib;
1273 
1274     uint rc = 1;
1275     size_t nfop; // hide it from GC
1276 
1277     private @property inout(Nfo*) nfo () inout pure nothrow @trusted @nogc { pragma(inline, true); return cast(typeof(return))nfop; }
1278     static void decRef (size_t me) {
1279       if (me) {
1280         auto zl = cast(LowLevelPackedRO*)me;
1281         assert(zl.rc);
1282         if (--zl.rc == 0) {
1283           //import core.stdc.stdlib : free;
1284           if (zl.chunkData !is null) xfree(zl.chunkData);
1285           version(arcz_use_more_memory) if (zl.pkdata !is null) xfree(zl.pkdata);
1286           Nfo.decRef(zl.nfop);
1287           xfree(zl);
1288           debug(arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p freed\n", zl); }
1289         } else {
1290           //debug(arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p; rc after decRef is %u\n", zl, zl.rc); }
1291         }
1292       }
1293     }
1294 
1295     uint nextchunk; // next chunk to read
1296     uint curcpos; // position in current chunk
1297     uint curcsize; // number of valid bytes in `chunkData`
1298     uint stchunk; // starting chunk
1299     uint stofs; // offset in starting chunk
1300     uint totalsize; // total file size
1301     uint pos; // current file position
1302     uint lastrdpos; // last actual read position
1303     z_stream zs;
1304     ubyte* chunkData; // can be null
1305     version(arcz_use_more_memory) {
1306       ubyte* pkdata;
1307       uint pkdatasize;
1308     }
1309 
1310     @disable this (this);
1311 
1312     void setup (Nfo* anfo, uint astchunk, uint astofs, uint asize) {
1313       assert(anfo !is null);
1314       assert(rc == 1);
1315       nfop = cast(size_t)anfo;
1316       ++anfo.rc;
1317       nextchunk = stchunk = astchunk;
1318       //curcpos = 0;
1319       stofs = astofs;
1320       totalsize = asize;
1321     }
1322 
1323     @property bool eof () { pragma(inline, true); return (pos >= totalsize); }
1324 
1325     // return less than chunk size if our file fits in one non-full chunk completely
1326     uint justEnoughMemory () pure const nothrow @safe @nogc {
1327       pragma(inline, true);
1328       version(none) {
1329         return nfo.chunkSize;
1330       } else {
1331         return (totalsize < nfo.chunkSize && stofs+totalsize < nfo.chunkSize ? stofs+totalsize : nfo.chunkSize);
1332       }
1333     }
1334 
1335     void unpackNextChunk () @system {
1336       if (nfop == 0) assert(0, "wtf?!");
1337       //scope(failure) if (chunkData !is null) { xfree(chunkData); chunkData = null; }
1338       debug(arcz_unp) { import core.stdc.stdio : printf; printf("unpacking chunk %u\n", nextchunk); }
1339       // allocate buffer for unpacked data
1340       if (chunkData is null) {
1341         // optimize things a little: if our file fits in less then one chunk, allocate "just enough" memory
1342         chunkData = xalloc!(ubyte, false)(justEnoughMemory);
1343       }
1344       auto chunk = &nfo.chunks[nextchunk];
1345       if (chunk.pksize == nfo.chunkSize) {
1346         // unpacked chunk, just read it
1347         debug(arcz_unp) { import core.stdc.stdio : printf; printf(" chunk is not packed\n"); }
1348         if (fseek(nfo.afl, chunk.ofs, 0) < 0) throw new Exception("ARCZ reading error");
1349         if (fread(chunkData, 1, nfo.chunkSize, nfo.afl) != nfo.chunkSize) throw new Exception("ARCZ reading error");
1350         curcsize = nfo.chunkSize;
1351       } else {
1352         // packed chunk, unpack it
1353         // allocate buffer for packed data
1354         version(arcz_use_more_memory) {
1355           import core.stdc.stdlib : realloc;
1356           if (pkdatasize < chunk.pksize) {
1357             import core.exception : onOutOfMemoryError;
1358             auto newpk = realloc(pkdata, chunk.pksize);
1359             if (newpk is null) onOutOfMemoryError();
1360             debug(arcz_alloc) { import core.stdc.stdio : printf; printf("reallocated from %u to %u bytes; %p -> %p\n", cast(uint)pkdatasize, cast(uint)chunk.pksize, pkdata, newpk); }
1361             pkdata = cast(ubyte*)newpk;
1362             pkdatasize = chunk.pksize;
1363           }
1364           alias pkd = pkdata;
1365         } else {
1366           auto pkd = xalloc!(ubyte, false)(chunk.pksize);
1367           scope(exit) xfree(pkd);
1368         }
1369         if (fseek(nfo.afl, chunk.ofs, 0) < 0) throw new Exception("ARCZ reading error");
1370         if (fread(pkd, 1, chunk.pksize, nfo.afl) != chunk.pksize) throw new Exception("ARCZ reading error");
1371         uint upsize = (nextchunk == nfo.chunks.length-1 ? nfo.lastChunkSize : nfo.chunkSize); // unpacked chunk size
1372         immutable uint cksz = upsize;
1373         immutable uint jem = justEnoughMemory;
1374         if (upsize > jem) upsize = jem;
1375         debug(arcz_unp) { import core.stdc.stdio : printf; printf(" unpacking %u bytes to %u bytes\n", chunk.pksize, upsize); }
1376         ArzArchive.unpackBlock(chunkData, upsize, pkd, chunk.pksize, cksz, nfo.useBalz);
1377         curcsize = upsize;
1378       }
1379       curcpos = 0;
1380       // fix first chunk offset if necessary
1381       if (nextchunk == stchunk && stofs > 0) {
1382         // it's easier to just memmove it
1383         import core.stdc.string : memmove;
1384         assert(stofs < curcsize);
1385         memmove(chunkData, chunkData+stofs, curcsize-stofs);
1386         curcsize -= stofs;
1387       }
1388       ++nextchunk; // advance to next chunk
1389     }
1390 
1391     void syncReadPos () {
1392       if (pos >= totalsize || pos == lastrdpos) return;
1393       immutable uint fcdata = nfo.chunkSize-stofs; // number of our bytes in the first chunk
1394       // does our pos lie in the first chunk?
1395       if (pos < fcdata) {
1396         // yep, just read it
1397         if (nextchunk != stchunk+1) {
1398           nextchunk = stchunk;
1399           unpackNextChunk(); // we'll need it anyway
1400         } else {
1401           // just rewind
1402           curcpos = 0;
1403         }
1404         curcpos += pos;
1405         lastrdpos = pos;
1406         return;
1407       }
1408       // find the chunk we want
1409       uint npos = pos-fcdata;
1410       uint xblock = stchunk+1+npos/nfo.chunkSize;
1411       uint curcstart = (xblock-(stchunk+1))*nfo.chunkSize+fcdata;
1412       if (xblock != nextchunk-1) {
1413         // read and unpack this chunk
1414         nextchunk = xblock;
1415         unpackNextChunk();
1416       } else {
1417         // just rewind
1418         curcpos = 0;
1419       }
1420       assert(pos >= curcstart && pos < curcstart+nfo.chunkSize);
1421       uint skip = pos-curcstart;
1422       lastrdpos = pos;
1423       curcpos += skip;
1424     }
1425 
1426     int read (void* buf, uint count) @system {
1427       if (buf is null) return -1;
1428       if (count == 0 || totalsize == 0) return 0;
1429       if (totalsize >= 0 && pos >= totalsize) return 0; // EOF
1430       syncReadPos();
1431       assert(lastrdpos == pos);
1432       if (cast(long)pos+count > totalsize) count = totalsize-pos;
1433       auto res = count;
1434       while (count > 0) {
1435         debug(arcz_read) { import core.stdc.stdio : printf; printf("reading %u bytes; pos=%u; lastrdpos=%u; curcpos=%u; curcsize=%u\n", count, pos, lastrdpos, curcpos, curcsize); }
1436         import core.stdc.string : memcpy;
1437         if (curcpos >= curcsize) {
1438           unpackNextChunk(); // we want next chunk!
1439           debug(arcz_read) { import core.stdc.stdio : printf; printf(" *reading %u bytes; pos=%u; lastrdpos=%u; curcpos=%u; curcsize=%u\n", count, pos, lastrdpos, curcpos, curcsize); }
1440         }
1441         assert(curcpos < curcsize && curcsize != 0);
1442         int rd = (curcsize-curcpos >= count ? count : curcsize-curcpos);
1443         assert(rd > 0);
1444         memcpy(buf, chunkData+curcpos, rd);
1445         curcpos += rd;
1446         pos += rd;
1447         lastrdpos += rd;
1448         buf += rd;
1449         count -= rd;
1450       }
1451       assert(pos == lastrdpos);
1452       return res;
1453     }
1454 
1455     long lseek (long ofs, int origin) {
1456       //TODO: overflow checks
1457       switch (origin) {
1458         case SEEK_SET: break;
1459         case SEEK_CUR: ofs += pos; break;
1460         case SEEK_END:
1461           if (ofs > 0) ofs = 0;
1462           if (-ofs > totalsize) ofs = -cast(long)totalsize;
1463           ofs += totalsize;
1464           break;
1465         default:
1466           return -1;
1467       }
1468       if (ofs < 0) return -1;
1469       if (totalsize >= 0 && ofs > totalsize) ofs = totalsize;
1470       pos = cast(uint)ofs;
1471       return pos;
1472     }
1473   }
1474 }
1475 
1476 
1477 // ////////////////////////////////////////////////////////////////////////// //
1478 /// Opened file.
1479 public struct AZFile {
1480 private:
1481   size_t zlp;
1482 
1483   private @property inout(ArzArchive.LowLevelPackedRO)* zl () inout pure nothrow @trusted @nogc { pragma(inline, true); return cast(typeof(return))zlp; }
1484   private void decRef () { pragma(inline, true); ArzArchive.LowLevelPackedRO.decRef(zlp); zlp = 0; }
1485 
1486 public:
1487   this (in AZFile afl) {
1488     assert(zlp == 0);
1489     zlp = afl.zlp;
1490     if (zlp) ++zl.rc;
1491   }
1492 
1493   this (this) {
1494     if (zlp) ++zl.rc;
1495   }
1496 
1497   ~this () { close(); }
1498 
1499   void opAssign (in AZFile afl) {
1500     if (afl.zlp) {
1501       auto n = cast(ArzArchive.LowLevelPackedRO*)afl.zlp;
1502       ++n.rc;
1503     }
1504     decRef();
1505     zlp = afl.zlp;
1506   }
1507 
1508   void close () { decRef(); }
1509 
1510   @property bool isOpen () const pure nothrow @safe @nogc { pragma(inline, true); return (zlp != 0); }
1511   @property uint size () const pure nothrow @safe @nogc { pragma(inline, true); return (zlp ? zl.totalsize : 0); }
1512   @property uint tell () const pure nothrow @safe @nogc { pragma(inline, true); return (zlp ? zl.pos : 0); }
1513 
1514   void seek (long ofs, int origin=SEEK_SET) {
1515     if (!zlp) throw new Exception("can't seek in closed file");
1516     auto res = zl.lseek(ofs, origin);
1517     if (res < 0) throw new Exception("seek error");
1518   }
1519 
1520   //TODO: overflow check
1521   T[] rawRead(T) (T[] buf) {
1522     if (!zlp) throw new Exception("can't read from closed file");
1523     if (buf.length > 0) {
1524       auto res = zl.read(buf.ptr, cast(int) (buf.length*T.sizeof));
1525       if (res == -1 || res%T.sizeof != 0) throw new Exception("read error");
1526       return buf[0..res/T.sizeof];
1527     } else {
1528       return buf[0..0];
1529     }
1530   }
1531 }
1532 
1533 
1534 // ////////////////////////////////////////////////////////////////////////// //
1535 /** this class can be used to create archive file.
1536  *
1537  * Example:
1538  * --------------------
1539  *  import std.file, std.path, std.stdio : File;
1540  *
1541  *  enum ArcName = "z00.arz";
1542  *  enum DirName = "experimental-docs";
1543  *
1544  *  ubyte[] rdbuf;
1545  *  rdbuf.length = 65536;
1546  *
1547  *  auto arcz = new ArzCreator(ArcName);
1548  *  long total = 0;
1549  *  foreach (DirEntry e; dirEntries(DirName, SpanMode.breadth)) {
1550  *    if (e.isFile) {
1551  *      assert(e.size < uint.max);
1552  *      //writeln(e.name);
1553  *      total += e.size;
1554  *      string fname = e.name[DirName.length+1..$];
1555  *      arcz.newFile(fname, cast(uint)e.size);
1556  *      auto fi = File(e.name);
1557  *      for (;;) {
1558  *        auto rd = fi.rawRead(rdbuf[]);
1559  *        if (rd.length == 0) break;
1560  *        arcz.rawWrite(rd[]);
1561  *      }
1562  *    }
1563  *  }
1564  *  arcz.close();
1565  *  writeln(total, " bytes packed to ", getSize(ArcName), " (", arcz.chunksWritten, " chunks, ", arcz.filesWritten, " files)");
1566  * --------------------
1567  */
1568 final class ArzCreator {
1569 private import etc.c.zlib;
1570 private import core.stdc.stdio : FILE, fopen, fclose, ftell, fseek, fwrite;
1571 
1572 public:
1573   //WARNING! don't change the order!
1574   enum Compressor {
1575     ZLib, // default
1576     Balz,
1577     BalzMax, // Balz, maximum compression
1578     Zopfli, // this will fallback to zlib if no zopfli support was compiled in
1579   }
1580 
1581 private:
1582   static struct ChunkInfo {
1583     uint ofs; // offset in file
1584     uint pksize; // packed chunk size
1585   }
1586 
1587   static struct FileInfo {
1588     string name;
1589     uint chunk;
1590     uint chunkofs; // offset of first file byte in unpacked chunk
1591     uint size; // unpacked file size
1592   }
1593 
1594 private:
1595   ubyte[] chunkdata;
1596   uint cdpos;
1597   FILE* arcfl;
1598   ChunkInfo[] chunks;
1599   FileInfo[] files;
1600   uint lastChunkSize;
1601   uint statChunks, statFiles;
1602   Compressor cpr = Compressor.ZLib;
1603 
1604 private:
1605   void writeUint (uint v) {
1606     if (arcfl is null) throw new Exception("write error");
1607     version(BigEndian) {
1608       import core.bitop : bswap;
1609       v = bswap(v);
1610     } else version(LittleEndian) {
1611       // nothing to do
1612     } else {
1613       static assert(0, "wtf?!");
1614     }
1615     if (fwrite(&v, 1, v.sizeof, arcfl) != v.sizeof) throw new Exception("write error"); // signature
1616   }
1617 
1618   void writeUbyte (ubyte v) {
1619     if (arcfl is null) throw new Exception("write error");
1620     if (fwrite(&v, 1, v.sizeof, arcfl) != v.sizeof) throw new Exception("write error"); // signature
1621   }
1622 
1623   void writeBuf (const(void)[] buf) {
1624     if (buf.length > 0) {
1625       if (arcfl is null) throw new Exception("write error");
1626       if (fwrite(buf.ptr, 1, buf.length, arcfl) != buf.length) throw new Exception("write error"); // signature
1627     }
1628   }
1629 
1630   static if (arcz_has_balz) long writePackedBalz (const(void)[] upbuf) {
1631     assert(upbuf.length > 0 && upbuf.length < int.max);
1632     long res = 0;
1633     Balz bz;
1634     int ipos, opos;
1635     bz.reinit(ArzArchive.balzDictSize(cast(uint)upbuf.length));
1636     bz.compress(
1637       // reader
1638       (buf) {
1639         import core.stdc.string : memcpy;
1640         if (ipos >= upbuf.length) return 0;
1641         uint rd = cast(uint)upbuf.length-ipos;
1642         if (rd > buf.length) rd = cast(uint)buf.length;
1643         memcpy(buf.ptr, upbuf.ptr+ipos, rd);
1644         ipos += rd;
1645         return rd;
1646       },
1647       // writer
1648       (buf) {
1649         res += buf.length;
1650         writeBuf(buf[]);
1651       },
1652       // max mode
1653       (cpr == Compressor.BalzMax)
1654     );
1655     return res;
1656   }
1657 
1658   static if (arcz_has_zopfli) long writePackedZopfli (const(void)[] upbuf) {
1659     ubyte[] indata;
1660     void* odata;
1661     size_t osize;
1662     ZopfliOptions opts;
1663     ZopfliCompress(opts, ZOPFLI_FORMAT_ZLIB, upbuf.ptr, upbuf.length, &odata, &osize);
1664     writeBuf(odata[0..osize]);
1665     ZopfliFree(odata);
1666     return cast(long)osize;
1667   }
1668 
1669   long writePackedZLib (const(void)[] upbuf) {
1670     assert(upbuf.length > 0 && upbuf.length < int.max);
1671     long res = 0;
1672     z_stream zs;
1673     ubyte[2048] obuf;
1674     zs.next_out = obuf.ptr;
1675     zs.avail_out = cast(uint)obuf.length;
1676     zs.next_in = null;
1677     zs.avail_in = 0;
1678     // initialize packer
1679     if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, 15, 9, 0) != Z_OK) throw new Exception("can't write packed data");
1680     scope(exit) deflateEnd(&zs);
1681     zs.next_in = cast(typeof(zs.next_in))upbuf.ptr;
1682     zs.avail_in = cast(uint)upbuf.length;
1683     while (zs.avail_in > 0) {
1684       if (zs.avail_out == 0) {
1685         res += cast(uint)obuf.length;
1686         writeBuf(obuf[]);
1687         zs.next_out = obuf.ptr;
1688         zs.avail_out = cast(uint)obuf.length;
1689       }
1690       auto err = deflate(&zs, Z_NO_FLUSH);
1691       if (err != Z_OK) throw new Exception("zlib compression error");
1692     }
1693     while (zs.avail_out != obuf.length) {
1694       res += cast(uint)obuf.length-zs.avail_out;
1695       writeBuf(obuf[0..$-zs.avail_out]);
1696       zs.next_out = obuf.ptr;
1697       zs.avail_out = cast(uint)obuf.length;
1698       auto err = deflate(&zs, Z_FINISH);
1699       if (err != Z_OK && err != Z_STREAM_END) throw new Exception("zlib compression error");
1700       // succesfully flushed?
1701       //if (err != Z_STREAM_END) throw new VFSException("zlib compression error");
1702     }
1703     return res;
1704   }
1705 
1706   // return size of packed data written
1707   uint writePackedBuf (const(void)[] upbuf) {
1708     assert(upbuf.length > 0 && upbuf.length < int.max);
1709     long res = 0;
1710     final switch (cpr) {
1711       case Compressor.ZLib:
1712         res = writePackedZLib(upbuf);
1713         break;
1714       case Compressor.Balz:
1715       case Compressor.BalzMax:
1716         static if (arcz_has_balz) {
1717           res = writePackedBalz(upbuf);
1718           break;
1719         } else {
1720           throw new Exception("no Balz support was compiled in ArcZ");
1721         }
1722       case Compressor.Zopfli:
1723         static if (arcz_has_zopfli) {
1724           res = writePackedZopfli(upbuf);
1725           //break;
1726         } else {
1727           //new Exception("no Zopfli support was compiled in ArcZ");
1728           res = writePackedZLib(upbuf);
1729         }
1730         break;
1731     }
1732     if (res > uint.max) throw new Exception("output archive too big");
1733     return cast(uint)res;
1734   }
1735 
1736   void flushData () {
1737     if (cdpos > 0) {
1738       ChunkInfo ci;
1739       auto pos = ftell(arcfl);
1740       if (pos < 0 || pos >= uint.max) throw new Exception("output archive too big");
1741       ci.ofs = cast(uint)pos;
1742       auto wlen = writePackedBuf(chunkdata[0..cdpos]);
1743       ci.pksize = wlen;
1744       if (cdpos == chunkdata.length && ci.pksize >= chunkdata.length) {
1745         // wow, this chunk is unpackable
1746         //{ import std.stdio; writeln("unpackable chunk found!"); }
1747         if (fseek(arcfl, pos, 0) < 0) throw new Exception("can't seek in output file");
1748         writeBuf(chunkdata[0..cdpos]);
1749         version(Posix) {
1750           import core.stdc.stdio : fileno;
1751           import core.sys.posix.unistd : ftruncate;
1752           pos = ftell(arcfl);
1753           if (pos < 0 || pos >= uint.max) throw new Exception("output archive too big");
1754           if (ftruncate(fileno(arcfl), cast(uint)pos) < 0) throw new Exception("error truncating output file");
1755         }
1756         ci.pksize = cdpos;
1757       }
1758       if (cdpos < chunkdata.length) lastChunkSize = cast(uint)cdpos;
1759       cdpos = 0;
1760       chunks ~= ci;
1761     } else {
1762       lastChunkSize = cast(uint)chunkdata.length;
1763     }
1764   }
1765 
1766   void closeArc () {
1767     flushData();
1768     // write index
1769     //assert(ftell(arcfl) > 0 && ftell(arcfl) < uint.max);
1770     assert(chunkdata.length < uint.max);
1771     assert(chunks.length < uint.max);
1772     assert(files.length < uint.max);
1773     // create index in memory
1774     ubyte[] index;
1775 
1776     void putUint (uint v) {
1777       index ~= v&0xff;
1778       index ~= (v>>8)&0xff;
1779       index ~= (v>>16)&0xff;
1780       index ~= (v>>24)&0xff;
1781     }
1782 
1783     void putUbyte (ubyte v) {
1784       index ~= v;
1785     }
1786 
1787     void putBuf (const(void)[] buf) {
1788       assert(buf.length > 0);
1789       index ~= (cast(const(ubyte)[])buf)[];
1790     }
1791 
1792     // create index in memory
1793     {
1794       // chunk size
1795       putUint(cast(uint)chunkdata.length);
1796       // chunk count
1797       putUint(cast(uint)chunks.length);
1798       // last chunk size
1799       putUint(lastChunkSize); // 0: last chunk is full
1800       // chunk offsets and sizes
1801       foreach (ref ci; chunks) {
1802         putUint(ci.ofs);
1803         putUint(ci.pksize);
1804       }
1805       // file count
1806       putUint(cast(uint)files.length);
1807       uint nbofs = cast(uint)index.length+cast(uint)files.length*(5*4);
1808       //uint nbofs = 0;
1809       // files
1810       foreach (ref fi; files) {
1811         // name: length(byte), chars
1812         assert(fi.name.length > 0 && fi.name.length <= 16384);
1813         putUint(nbofs);
1814         putUint(cast(uint)fi.name.length);
1815         nbofs += cast(uint)fi.name.length+1; // put zero byte there to ease C interfacing
1816         //putBuf(fi.name[]);
1817         // chunk number
1818         putUint(fi.chunk);
1819         // offset in unpacked chunk
1820         putUint(fi.chunkofs);
1821         // unpacked size
1822         putUint(fi.size);
1823       }
1824       // names
1825       foreach (ref fi; files) {
1826         putBuf(fi.name[]);
1827         putUbyte(0); // this means nothing, it is here just for convenience (hello, C!)
1828       }
1829       assert(index.length < uint.max);
1830     }
1831     auto cpos = ftell(arcfl);
1832     if (cpos < 0 || cpos > uint.max) throw new Exception("output archive too big");
1833     // write packed index
1834     debug(arcz_writer) { import core.stdc.stdio : pinrtf; printf("index size: %u\n", cast(uint)index.length); }
1835     auto pkisz = writePackedBuf(index[]);
1836     debug(arcz_writer) { import core.stdc.stdio : pinrtf; printf("packed index size: %u\n", cast(uint)pkisz); }
1837     // write index info
1838     if (fseek(arcfl, 5, 0) < 0) throw new Exception("seek error");
1839     // index offset in file
1840     writeUint(cast(uint) cpos);
1841     // packed index size
1842     writeUint(pkisz);
1843     // unpacked index size
1844     writeUint(cast(uint)index.length);
1845     // done
1846     statChunks = cast(uint)chunks.length;
1847     statFiles = cast(uint)files.length;
1848   }
1849 
1850 public:
1851   this (const(char)[] fname, uint chunkSize=256*1024, Compressor acpr=Compressor.ZLib) {
1852     assert(chunkSize > 0 && chunkSize < 32*1024*1024); // arbitrary limit
1853     static if (!arcz_has_balz) {
1854       if (acpr == Compressor.Balz || acpr == Compressor.BalzMax) throw new Exception("no Balz support was compiled in ArcZ");
1855     }
1856     static if (!arcz_has_zopfli) {
1857       //if (acpr == Compressor.Zopfli) throw new Exception("no Zopfli support was compiled in ArcZ");
1858     }
1859     cpr = acpr;
1860     arcfl = fopen((fname ~ "\0").ptr, "wb");
1861     if (arcfl is null) throw new Exception("can't create output file '"~fname.idup~"'");
1862     cdpos = 0;
1863     chunkdata.length = chunkSize;
1864     scope(failure) { fclose(arcfl); arcfl = null; }
1865     writeBuf("CZA2"); // signature
1866     if (cpr == Compressor.Balz || cpr == Compressor.BalzMax) {
1867       writeUbyte(1); // version
1868     } else {
1869       writeUbyte(0); // version
1870     }
1871     writeUint(0); // offset to index
1872     writeUint(0); // packed index size
1873     writeUint(0); // unpacked index size
1874   }
1875 
1876   ~this () { close(); }
1877 
1878   void close () {
1879     if (arcfl !is null) {
1880       scope(exit) { fclose(arcfl); arcfl = null; }
1881       closeArc();
1882     }
1883     chunkdata = null;
1884     chunks = null;
1885     files = null;
1886     lastChunkSize = 0;
1887     cdpos = 0;
1888   }
1889 
1890   // valid after closing
1891   @property uint chunksWritten () const pure nothrow @safe @nogc { pragma(inline, true); return statChunks; }
1892   @property uint filesWritten () const pure nothrow @safe @nogc { pragma(inline, true); return statFiles; }
1893 
1894   void newFile (string name, uint size) {
1895     FileInfo fi;
1896     assert(name.length <= 255);
1897     fi.name = name;
1898     fi.chunk = cast(uint)chunks.length;
1899     fi.chunkofs = cast(uint)cdpos;
1900     fi.size = size;
1901     files ~= fi;
1902   }
1903 
1904   void rawWrite(T) (const(T)[] buffer) {
1905     if (buffer.length > 0) {
1906       auto src = cast(const(ubyte)*)buffer.ptr;
1907       auto len = buffer.length*T.sizeof;
1908       while (len > 0) {
1909         if (cdpos == chunkdata.length) flushData();
1910         if (cdpos < chunkdata.length) {
1911           auto wr = chunkdata.length-cdpos;
1912           if (wr > len) wr = len;
1913           chunkdata[cdpos..cdpos+wr] = src[0..wr];
1914           cdpos += wr;
1915           len -= wr;
1916           src += wr;
1917         }
1918       }
1919     }
1920   }
1921 }
1922 
1923 
1924 // ////////////////////////////////////////////////////////////////////////// //
1925 /* arcz file format:
1926 header
1927 ======
1928 db 'CZA2'     ; signature
1929 db version    ; 0: zlib; 1: balz
1930 dd indexofs   ; offset to packed index
1931 dd pkindexsz  ; size of packed index
1932 dd upindexsz  ; size of unpacked index
1933 
1934 
1935 index
1936 =====
1937 dd chunksize    ; unpacked chunk size in bytes
1938 dd chunkcount   ; number of chunks in file
1939 dd lastchunksz  ; size of last chunk (it may be incomplete); 0: last chunk is completely used (all `chunksize` bytes)
1940 
1941 then chunk offsets and sizes follows:
1942   dd chunkofs   ; from file start
1943   dd pkchunksz  ; size of (possibly packed) chunk data; if it equals to `chunksize`, this chunk is not packed
1944 
1945 then file list follows:
1946 dd filecount  ; number of files in archive
1947 
1948 then file info follows:
1949   dd nameofs     ; (in index)
1950   dd namelen     ; length of name (can't be 0)
1951   dd firstchunk  ; chunk where file starts
1952   dd firstofs    ; offset in first chunk (unpacked) where file starts
1953   dd filesize    ; unpacked file size
1954 
1955 then name buffer follows -- just bytes
1956 */
1957 
1958 }
1959 
1960 version(WithLzmaDecoder) {
1961 
1962 /* *************************************************** */
1963 /* The rest of the file is copy/paste of external code */
1964 /* *************************************************** */
1965 
1966 /* LzmaDec.h -- LZMA Decoder
1967 2017-04-03 : Igor Pavlov : Public domain */
1968 // also by Lasse Collin
1969 /* ported to D by ketmar */
1970 private nothrow @trusted @nogc:
1971 
1972 //version = _LZMA_PROB32;
1973 /* _LZMA_PROB32 can increase the speed on some CPUs,
1974    but memory usage for CLzmaDec::probs will be doubled in that case */
1975 
1976 //version = _LZMA_SIZE_OPT;
1977 
1978 alias Byte = ubyte;
1979 alias UInt16 = ushort;
1980 alias UInt32 = uint;
1981 alias SizeT = size_t;
1982 
1983 version(_LZMA_PROB32) {
1984   alias CLzmaProb = UInt32;
1985 } else {
1986   alias CLzmaProb = UInt16;
1987 }
1988 
1989 public enum SRes {
1990   OK,
1991   ERROR_UNSUPPORTED,
1992   ERROR_MEM,
1993   ERROR_DATA,
1994   ERROR_INPUT_EOF,
1995   ERROR_FAIL,
1996 }
1997 
1998 /* ---------- LZMA Properties ---------- */
1999 
2000 public enum LZMA_PROPS_SIZE = 5;
2001 
2002 public struct CLzmaProps {
2003   uint lc, lp, pb;
2004   UInt32 dicSize;
2005 }
2006 
2007 /* LzmaProps_Decode - decodes properties
2008 Returns:
2009   SRes.OK
2010   SRes.ERROR_UNSUPPORTED - Unsupported properties
2011 */
2012 
2013 //!SRes LzmaProps_Decode(CLzmaProps *p, const(Byte)* data, uint size);
2014 
2015 
2016 /* ---------- LZMA Decoder state ---------- */
2017 
2018 /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
2019    Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
2020 
2021 enum LZMA_REQUIRED_INPUT_MAX = 20;
2022 
2023 public struct CLzmaDec {
2024 private:
2025   CLzmaProps prop;
2026   CLzmaProb* probs;
2027   public Byte* dic;
2028   const(Byte)* buf;
2029   UInt32 range, code;
2030   public SizeT dicPos;
2031   public SizeT dicBufSize;
2032   UInt32 processedPos;
2033   UInt32 checkDicSize;
2034   uint state;
2035   UInt32[4] reps;
2036   uint remainLen;
2037   int needFlush;
2038   int needInitState;
2039   UInt32 numProbs;
2040   uint tempBufSize;
2041   Byte[LZMA_REQUIRED_INPUT_MAX] tempBuf;
2042 }
2043 
2044 //#define LzmaDec_Construct(p) { (p).dic = 0; (p).probs = 0; }
2045 
2046 //void LzmaDec_Init(CLzmaDec *p);
2047 
2048 /* There are two types of LZMA streams:
2049      0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
2050      1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
2051 
2052 public alias ELzmaFinishMode = int;
2053 public enum /*ELzmaFinishMode*/ {
2054   LZMA_FINISH_ANY,   /* finish at any point */
2055   LZMA_FINISH_END    /* block must be finished at the end */
2056 }
2057 
2058 /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
2059 
2060    You must use LZMA_FINISH_END, when you know that current output buffer
2061    covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
2062 
2063    If LZMA decoder sees end marker before reaching output limit, it returns SRes.OK,
2064    and output value of destLen will be less than output buffer size limit.
2065    You can check status result also.
2066 
2067    You can use multiple checks to test data integrity after full decompression:
2068      1) Check Result and "status" variable.
2069      2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
2070      3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
2071         You must use correct finish mode in that case. */
2072 
2073 public alias ELzmaStatus = int;
2074 public enum /*ELzmaStatus*/ {
2075   LZMA_STATUS_NOT_SPECIFIED,               /* use main error code instead */
2076   LZMA_STATUS_FINISHED_WITH_MARK,          /* stream was finished with end mark. */
2077   LZMA_STATUS_NOT_FINISHED,                /* stream was not finished */
2078   LZMA_STATUS_NEEDS_MORE_INPUT,            /* you must provide more input bytes */
2079   LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK  /* there is probability that stream was finished without end mark */
2080 }
2081 
2082 /* ELzmaStatus is used only as output value for function call */
2083 
2084 
2085 /* ---------- Interfaces ---------- */
2086 
2087 /* There are 3 levels of interfaces:
2088      1) Dictionary Interface
2089      2) Buffer Interface
2090      3) One Call Interface
2091    You can select any of these interfaces, but don't mix functions from different
2092    groups for same object. */
2093 
2094 
2095 /* There are two variants to allocate state for Dictionary Interface:
2096      1) LzmaDec_Allocate / LzmaDec_Free
2097      2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
2098    You can use variant 2, if you set dictionary buffer manually.
2099    For Buffer Interface you must always use variant 1.
2100 
2101 LzmaDec_Allocate* can return:
2102   SRes.OK
2103   SRes.ERROR_MEM         - Memory allocation error
2104   SRes.ERROR_UNSUPPORTED - Unsupported properties
2105 */
2106 
2107 /*
2108 SRes LzmaDec_AllocateProbs(CLzmaDec *p, const(Byte)* props, uint propsSize);
2109 void LzmaDec_FreeProbs(CLzmaDec *p);
2110 
2111 SRes LzmaDec_Allocate(CLzmaDec *state, const(Byte)* prop, uint propsSize);
2112 void LzmaDec_Free(CLzmaDec *state);
2113 */
2114 
2115 /* ---------- Dictionary Interface ---------- */
2116 
2117 /* You can use it, if you want to eliminate the overhead for data copying from
2118    dictionary to some other external buffer.
2119    You must work with CLzmaDec variables directly in this interface.
2120 
2121    STEPS:
2122      LzmaDec_Constr()
2123      LzmaDec_Allocate()
2124      for (each new stream)
2125      {
2126        LzmaDec_Init()
2127        while (it needs more decompression)
2128        {
2129          LzmaDec_DecodeToDic()
2130          use data from CLzmaDec::dic and update CLzmaDec::dicPos
2131        }
2132      }
2133      LzmaDec_Free()
2134 */
2135 
2136 /* LzmaDec_DecodeToDic
2137 
2138    The decoding to internal dictionary buffer (CLzmaDec::dic).
2139    You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
2140 
2141 finishMode:
2142   It has meaning only if the decoding reaches output limit (dicLimit).
2143   LZMA_FINISH_ANY - Decode just dicLimit bytes.
2144   LZMA_FINISH_END - Stream must be finished after dicLimit.
2145 
2146 Returns:
2147   SRes.OK
2148     status:
2149       LZMA_STATUS_FINISHED_WITH_MARK
2150       LZMA_STATUS_NOT_FINISHED
2151       LZMA_STATUS_NEEDS_MORE_INPUT
2152       LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
2153   SRes.ERROR_DATA - Data error
2154 */
2155 
2156 //SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const(Byte)* src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
2157 
2158 
2159 /* ---------- Buffer Interface ---------- */
2160 
2161 /* It's zlib-like interface.
2162    See LzmaDec_DecodeToDic description for information about STEPS and return results,
2163    but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
2164    to work with CLzmaDec variables manually.
2165 
2166 finishMode:
2167   It has meaning only if the decoding reaches output limit (*destLen).
2168   LZMA_FINISH_ANY - Decode just destLen bytes.
2169   LZMA_FINISH_END - Stream must be finished after (*destLen).
2170 */
2171 
2172 //SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
2173 
2174 
2175 /* ---------- One Call Interface ---------- */
2176 
2177 /* LzmaDecode
2178 
2179 finishMode:
2180   It has meaning only if the decoding reaches output limit (*destLen).
2181   LZMA_FINISH_ANY - Decode just destLen bytes.
2182   LZMA_FINISH_END - Stream must be finished after (*destLen).
2183 
2184 Returns:
2185   SRes.OK
2186     status:
2187       LZMA_STATUS_FINISHED_WITH_MARK
2188       LZMA_STATUS_NOT_FINISHED
2189       LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
2190   SRes.ERROR_DATA - Data error
2191   SRes.ERROR_MEM  - Memory allocation error
2192   SRes.ERROR_UNSUPPORTED - Unsupported properties
2193   SRes.ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
2194 */
2195 
2196 /*
2197 SRes LzmaDecode(Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen,
2198     const(Byte)* propData, uint propSize, ELzmaFinishMode finishMode,
2199     ELzmaStatus *status, ISzAllocPtr alloc);
2200 */
2201 
2202 // ////////////////////////////////////////////////////////////////////////// //
2203 private:
2204 
2205 enum kNumTopBits = 24;
2206 enum kTopValue = 1U<<kNumTopBits;
2207 
2208 enum kNumBitModelTotalBits = 11;
2209 enum kBitModelTotal = 1<<kNumBitModelTotalBits;
2210 enum kNumMoveBits = 5;
2211 
2212 enum RC_INIT_SIZE = 5;
2213 
2214 //enum NORMALIZE = "if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }";
2215 
2216 //#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2217 //#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
2218 //#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
2219 enum GET_BIT2(string p, string i, string A0, string A1) =
2220   "ttt = *("~p~"); if (range < kTopValue) { range <<= 8; code = (code<<8)|(*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)\n"~
2221   "{ range = bound; *("~p~") = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); "~i~" = ("~i~"+"~i~"); "~A0~" } else\n"~
2222   "{ range -= bound; code -= bound; *("~p~") = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); "~i~" = ("~i~"+"~i~")+1; "~A1~" }";
2223 //#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;)
2224 enum GET_BIT(string p, string i) = GET_BIT2!(p, i, "", "");
2225 
2226 enum TREE_GET_BIT(string probs, string i) = "{"~GET_BIT!("("~probs~"+"~i~")", i)~"}";
2227 
2228 enum TREE_DECODE(string probs, string limit, string i) =
2229   "{ "~i~" = 1; do { "~TREE_GET_BIT!(probs, i)~" } while ("~i~" < "~limit~"); "~i~" -= "~limit~"; }";
2230 
2231 
2232 version(_LZMA_SIZE_OPT) {
2233   enum TREE_6_DECODE(string probs, string i) = TREE_DECODE!(probs, "(1<<6)", i);
2234 } else {
2235 enum TREE_6_DECODE(string probs, string i) =
2236   "{ "~i~" = 1;\n"~
2237   TREE_GET_BIT!(probs, i)~
2238   TREE_GET_BIT!(probs, i)~
2239   TREE_GET_BIT!(probs, i)~
2240   TREE_GET_BIT!(probs, i)~
2241   TREE_GET_BIT!(probs, i)~
2242   TREE_GET_BIT!(probs, i)~
2243   i~" -= 0x40; }";
2244 }
2245 
2246 enum NORMAL_LITER_DEC = GET_BIT!("prob + symbol", "symbol");
2247 enum MATCHED_LITER_DEC =
2248   "matchByte <<= 1;\n"~
2249   "bit = (matchByte & offs);\n"~
2250   "probLit = prob + offs + bit + symbol;\n"~
2251   GET_BIT2!("probLit", "symbol", "offs &= ~bit;", "offs &= bit;");
2252 
2253 enum NORMALIZE_CHECK = "if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }";
2254 
2255 //#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2256 //#define UPDATE_0_CHECK range = bound;
2257 //#define UPDATE_1_CHECK range -= bound; code -= bound;
2258 enum GET_BIT2_CHECK(string p, string i, string A0, string A1) =
2259   "ttt = *("~p~"); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)\n"~
2260   "{ range = bound; "~i~" = ("~i~" + "~i~"); "~A0~" } else\n"~
2261   "{ range -= bound; code -= bound; "~i~" = ("~i~" + "~i~") + 1; "~A1~" }";
2262 enum GET_BIT_CHECK(string p, string i) = GET_BIT2_CHECK!(p, i, "{}", "{}");
2263 enum TREE_DECODE_CHECK(string probs, string limit, string i) =
2264   "{ "~i~" = 1; do { "~GET_BIT_CHECK!(probs~"+"~i, i)~" } while ("~i~" < "~limit~"); "~i~" -= "~limit~"; }";
2265 
2266 
2267 enum kNumPosBitsMax = 4;
2268 enum kNumPosStatesMax = (1 << kNumPosBitsMax);
2269 
2270 enum kLenNumLowBits = 3;
2271 enum kLenNumLowSymbols = (1 << kLenNumLowBits);
2272 enum kLenNumMidBits = 3;
2273 enum kLenNumMidSymbols = (1 << kLenNumMidBits);
2274 enum kLenNumHighBits = 8;
2275 enum kLenNumHighSymbols = (1 << kLenNumHighBits);
2276 
2277 enum LenChoice = 0;
2278 enum LenChoice2 = (LenChoice + 1);
2279 enum LenLow = (LenChoice2 + 1);
2280 enum LenMid = (LenLow + (kNumPosStatesMax << kLenNumLowBits));
2281 enum LenHigh = (LenMid + (kNumPosStatesMax << kLenNumMidBits));
2282 enum kNumLenProbs = (LenHigh + kLenNumHighSymbols);
2283 
2284 
2285 enum kNumStates = 12;
2286 enum kNumLitStates = 7;
2287 
2288 enum kStartPosModelIndex = 4;
2289 enum kEndPosModelIndex = 14;
2290 enum kNumFullDistances = (1 << (kEndPosModelIndex >> 1));
2291 
2292 enum kNumPosSlotBits = 6;
2293 enum kNumLenToPosStates = 4;
2294 
2295 enum kNumAlignBits = 4;
2296 enum kAlignTableSize = (1 << kNumAlignBits);
2297 
2298 enum kMatchMinLen = 2;
2299 enum kMatchSpecLenStart = (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols);
2300 
2301 enum IsMatch = 0;
2302 enum IsRep = (IsMatch + (kNumStates << kNumPosBitsMax));
2303 enum IsRepG0 = (IsRep + kNumStates);
2304 enum IsRepG1 = (IsRepG0 + kNumStates);
2305 enum IsRepG2 = (IsRepG1 + kNumStates);
2306 enum IsRep0Long = (IsRepG2 + kNumStates);
2307 enum PosSlot = (IsRep0Long + (kNumStates << kNumPosBitsMax));
2308 enum SpecPos = (PosSlot + (kNumLenToPosStates << kNumPosSlotBits));
2309 enum Align = (SpecPos + kNumFullDistances - kEndPosModelIndex);
2310 enum LenCoder = (Align + kAlignTableSize);
2311 enum RepLenCoder = (LenCoder + kNumLenProbs);
2312 enum Literal = (RepLenCoder + kNumLenProbs);
2313 
2314 enum LZMA_BASE_SIZE = 1846;
2315 enum LZMA_LIT_SIZE = 0x300;
2316 
2317 static assert(Literal == LZMA_BASE_SIZE);
2318 
2319 //#define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p).lc + (p).lp)))
2320 
2321 enum LZMA_DIC_MIN = (1 << 12);
2322 
2323 /* First LZMA-symbol is always decoded.
2324 And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization
2325 Out:
2326   Result:
2327     SRes.OK - OK
2328     SRes.ERROR_DATA - Error
2329   p->remainLen:
2330     < kMatchSpecLenStart : normal remain
2331     = kMatchSpecLenStart : finished
2332     = kMatchSpecLenStart + 1 : Flush marker (unused now)
2333     = kMatchSpecLenStart + 2 : State Init Marker (unused now)
2334 */
2335 
2336 private SRes LzmaDec_DecodeReal (CLzmaDec* p, SizeT limit, const(Byte)* bufLimit) {
2337   CLzmaProb* probs = p.probs;
2338 
2339   uint state = p.state;
2340   UInt32 rep0 = p.reps.ptr[0], rep1 = p.reps.ptr[1], rep2 = p.reps.ptr[2], rep3 = p.reps.ptr[3];
2341   uint pbMask = (1U<<(p.prop.pb))-1;
2342   uint lpMask = (1U<<(p.prop.lp))-1;
2343   uint lc = p.prop.lc;
2344 
2345   Byte* dic = p.dic;
2346   SizeT dicBufSize = p.dicBufSize;
2347   SizeT dicPos = p.dicPos;
2348 
2349   UInt32 processedPos = p.processedPos;
2350   UInt32 checkDicSize = p.checkDicSize;
2351   uint len = 0;
2352 
2353   const(Byte)* buf = p.buf;
2354   UInt32 range = p.range;
2355   UInt32 code = p.code;
2356 
2357   do {
2358     CLzmaProb *prob;
2359     UInt32 bound;
2360     uint ttt;
2361     uint posState = processedPos & pbMask;
2362 
2363     prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
2364     ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2365     {
2366       uint symbol;
2367       range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2368       prob = probs + Literal;
2369       if (processedPos != 0 || checkDicSize != 0)
2370         prob += (cast(UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) +
2371             (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc))));
2372       processedPos++;
2373 
2374       if (state < kNumLitStates)
2375       {
2376         state -= (state < 4) ? state : 3;
2377         symbol = 1;
2378         version(_LZMA_SIZE_OPT) {
2379           do { mixin(NORMAL_LITER_DEC); } while (symbol < 0x100);
2380         } else {
2381           mixin(NORMAL_LITER_DEC);
2382           mixin(NORMAL_LITER_DEC);
2383           mixin(NORMAL_LITER_DEC);
2384           mixin(NORMAL_LITER_DEC);
2385           mixin(NORMAL_LITER_DEC);
2386           mixin(NORMAL_LITER_DEC);
2387           mixin(NORMAL_LITER_DEC);
2388           mixin(NORMAL_LITER_DEC);
2389         }
2390       }
2391       else
2392       {
2393         uint matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)];
2394         uint offs = 0x100;
2395         state -= (state < 10) ? 3 : 6;
2396         symbol = 1;
2397         version(_LZMA_SIZE_OPT) {
2398           do
2399           {
2400             uint bit;
2401             CLzmaProb *probLit;
2402             mixin(MATCHED_LITER_DEC);
2403           }
2404           while (symbol < 0x100);
2405         } else {
2406           {
2407             uint bit;
2408             CLzmaProb *probLit;
2409             mixin(MATCHED_LITER_DEC);
2410             mixin(MATCHED_LITER_DEC);
2411             mixin(MATCHED_LITER_DEC);
2412             mixin(MATCHED_LITER_DEC);
2413             mixin(MATCHED_LITER_DEC);
2414             mixin(MATCHED_LITER_DEC);
2415             mixin(MATCHED_LITER_DEC);
2416             mixin(MATCHED_LITER_DEC);
2417           }
2418         }
2419       }
2420 
2421       dic[dicPos++] = cast(Byte)symbol;
2422       continue;
2423     }
2424 
2425     {
2426       range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2427       prob = probs + IsRep + state;
2428       ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2429       {
2430         range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2431         state += kNumStates;
2432         prob = probs + LenCoder;
2433       }
2434       else
2435       {
2436         range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2437         if (checkDicSize == 0 && processedPos == 0)
2438           return SRes.ERROR_DATA;
2439         prob = probs + IsRepG0 + state;
2440         ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2441         {
2442           range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2443           prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
2444           ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2445           {
2446             range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2447             dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)];
2448             dicPos++;
2449             processedPos++;
2450             state = state < kNumLitStates ? 9 : 11;
2451             continue;
2452           }
2453           range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2454         }
2455         else
2456         {
2457           UInt32 distance;
2458           range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2459           prob = probs + IsRepG1 + state;
2460           ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2461           {
2462             range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2463             distance = rep1;
2464           }
2465           else
2466           {
2467             range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2468             prob = probs + IsRepG2 + state;
2469             ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2470             {
2471               range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2472               distance = rep2;
2473             }
2474             else
2475             {
2476               range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2477               distance = rep3;
2478               rep3 = rep2;
2479             }
2480             rep2 = rep1;
2481           }
2482           rep1 = rep0;
2483           rep0 = distance;
2484         }
2485         state = state < kNumLitStates ? 8 : 11;
2486         prob = probs + RepLenCoder;
2487       }
2488 
2489       version(_LZMA_SIZE_OPT) {
2490         {
2491           uint lim, offset;
2492           CLzmaProb *probLen = prob + LenChoice;
2493           ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2494           {
2495             range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2496             probLen = prob + LenLow + (posState << kLenNumLowBits);
2497             offset = 0;
2498             lim = (1 << kLenNumLowBits);
2499           }
2500           else
2501           {
2502             range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2503             probLen = prob + LenChoice2;
2504             ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2505             {
2506               range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2507               probLen = prob + LenMid + (posState << kLenNumMidBits);
2508               offset = kLenNumLowSymbols;
2509               lim = (1 << kLenNumMidBits);
2510             }
2511             else
2512             {
2513               range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2514               probLen = prob + LenHigh;
2515               offset = kLenNumLowSymbols + kLenNumMidSymbols;
2516               lim = (1 << kLenNumHighBits);
2517             }
2518           }
2519           mixin(TREE_DECODE!("probLen", "lim", "len"));
2520           len += offset;
2521         }
2522       } else {
2523         {
2524           CLzmaProb *probLen = prob + LenChoice;
2525           ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2526           {
2527             range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2528             probLen = prob + LenLow + (posState << kLenNumLowBits);
2529             len = 1;
2530             mixin(TREE_GET_BIT!("probLen", "len"));
2531             mixin(TREE_GET_BIT!("probLen", "len"));
2532             mixin(TREE_GET_BIT!("probLen", "len"));
2533             len -= 8;
2534           }
2535           else
2536           {
2537             range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2538             probLen = prob + LenChoice2;
2539             ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)
2540             {
2541               range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits));
2542               probLen = prob + LenMid + (posState << kLenNumMidBits);
2543               len = 1;
2544               mixin(TREE_GET_BIT!("probLen", "len"));
2545               mixin(TREE_GET_BIT!("probLen", "len"));
2546               mixin(TREE_GET_BIT!("probLen", "len"));
2547             }
2548             else
2549             {
2550               range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits));
2551               probLen = prob + LenHigh;
2552               mixin(TREE_DECODE!("probLen", "(1 << kLenNumHighBits)", "len"));
2553               len += kLenNumLowSymbols + kLenNumMidSymbols;
2554             }
2555           }
2556         }
2557       }
2558 
2559       if (state >= kNumStates)
2560       {
2561         UInt32 distance;
2562         prob = probs + PosSlot +
2563             ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
2564         mixin(TREE_6_DECODE!("prob", "distance"));
2565         if (distance >= kStartPosModelIndex)
2566         {
2567           uint posSlot = cast(uint)distance;
2568           uint numDirectBits = cast(uint)(((distance >> 1) - 1));
2569           distance = (2 | (distance & 1));
2570           if (posSlot < kEndPosModelIndex)
2571           {
2572             distance <<= numDirectBits;
2573             prob = probs + SpecPos + distance - posSlot - 1;
2574             {
2575               UInt32 mask = 1;
2576               uint i = 1;
2577               do
2578               {
2579                 mixin(GET_BIT2!("prob + i", "i", "{}" , "distance |= mask;"));
2580                 mask <<= 1;
2581               }
2582               while (--numDirectBits != 0);
2583             }
2584           }
2585           else
2586           {
2587             numDirectBits -= kNumAlignBits;
2588             do
2589             {
2590               if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
2591               range >>= 1;
2592 
2593               {
2594                 UInt32 t;
2595                 code -= range;
2596                 t = (0 - (cast(UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
2597                 distance = (distance << 1) + (t + 1);
2598                 code += range & t;
2599               }
2600               /*
2601               distance <<= 1;
2602               if (code >= range)
2603               {
2604                 code -= range;
2605                 distance |= 1;
2606               }
2607               */
2608             }
2609             while (--numDirectBits != 0);
2610             prob = probs + Align;
2611             distance <<= kNumAlignBits;
2612             {
2613               uint i = 1;
2614               mixin(GET_BIT2!("prob + i", "i", "", "distance |= 1;"));
2615               mixin(GET_BIT2!("prob + i", "i", "", "distance |= 2;"));
2616               mixin(GET_BIT2!("prob + i", "i", "", "distance |= 4;"));
2617               mixin(GET_BIT2!("prob + i", "i", "", "distance |= 8;"));
2618             }
2619             if (distance == cast(UInt32)0xFFFFFFFF)
2620             {
2621               len += kMatchSpecLenStart;
2622               state -= kNumStates;
2623               break;
2624             }
2625           }
2626         }
2627 
2628         rep3 = rep2;
2629         rep2 = rep1;
2630         rep1 = rep0;
2631         rep0 = distance + 1;
2632         if (checkDicSize == 0)
2633         {
2634           if (distance >= processedPos)
2635           {
2636             p.dicPos = dicPos;
2637             return SRes.ERROR_DATA;
2638           }
2639         }
2640         else if (distance >= checkDicSize)
2641         {
2642           p.dicPos = dicPos;
2643           return SRes.ERROR_DATA;
2644         }
2645         state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
2646       }
2647 
2648       len += kMatchMinLen;
2649 
2650       {
2651         SizeT rem;
2652         uint curLen;
2653         SizeT pos;
2654 
2655         if ((rem = limit - dicPos) == 0)
2656         {
2657           p.dicPos = dicPos;
2658           return SRes.ERROR_DATA;
2659         }
2660 
2661         curLen = ((rem < len) ? cast(uint)rem : len);
2662         pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0);
2663 
2664         processedPos += curLen;
2665 
2666         len -= curLen;
2667         if (curLen <= dicBufSize - pos)
2668         {
2669           Byte *dest = dic + dicPos;
2670           ptrdiff_t src = cast(ptrdiff_t)pos - cast(ptrdiff_t)dicPos;
2671           const(Byte)* lim = dest + curLen;
2672           dicPos += curLen;
2673           do
2674             *(dest) = cast(Byte)*(dest + src);
2675           while (++dest != lim);
2676         }
2677         else
2678         {
2679           do
2680           {
2681             dic[dicPos++] = dic[pos];
2682             if (++pos == dicBufSize)
2683               pos = 0;
2684           }
2685           while (--curLen != 0);
2686         }
2687       }
2688     }
2689   }
2690   while (dicPos < limit && buf < bufLimit);
2691 
2692   if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
2693 
2694   p.buf = buf;
2695   p.range = range;
2696   p.code = code;
2697   p.remainLen = len;
2698   p.dicPos = dicPos;
2699   p.processedPos = processedPos;
2700   p.reps.ptr[0] = rep0;
2701   p.reps.ptr[1] = rep1;
2702   p.reps.ptr[2] = rep2;
2703   p.reps.ptr[3] = rep3;
2704   p.state = state;
2705 
2706   return SRes.OK;
2707 }
2708 
2709 private void LzmaDec_WriteRem (CLzmaDec* p, SizeT limit) {
2710   if (p.remainLen != 0 && p.remainLen < kMatchSpecLenStart)
2711   {
2712     Byte *dic = p.dic;
2713     SizeT dicPos = p.dicPos;
2714     SizeT dicBufSize = p.dicBufSize;
2715     uint len = p.remainLen;
2716     SizeT rep0 = p.reps.ptr[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */
2717     SizeT rem = limit - dicPos;
2718     if (rem < len)
2719       len = cast(uint)(rem);
2720 
2721     if (p.checkDicSize == 0 && p.prop.dicSize - p.processedPos <= len)
2722       p.checkDicSize = p.prop.dicSize;
2723 
2724     p.processedPos += len;
2725     p.remainLen -= len;
2726     while (len != 0)
2727     {
2728       len--;
2729       dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)];
2730       dicPos++;
2731     }
2732     p.dicPos = dicPos;
2733   }
2734 }
2735 
2736 private SRes LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const(Byte)* bufLimit)
2737 {
2738   do
2739   {
2740     SizeT limit2 = limit;
2741     if (p.checkDicSize == 0)
2742     {
2743       UInt32 rem = p.prop.dicSize - p.processedPos;
2744       if (limit - p.dicPos > rem)
2745         limit2 = p.dicPos + rem;
2746     }
2747 
2748     if (auto sres = LzmaDec_DecodeReal(p, limit2, bufLimit)) return sres;
2749 
2750     if (p.checkDicSize == 0 && p.processedPos >= p.prop.dicSize)
2751       p.checkDicSize = p.prop.dicSize;
2752 
2753     LzmaDec_WriteRem(p, limit);
2754   }
2755   while (p.dicPos < limit && p.buf < bufLimit && p.remainLen < kMatchSpecLenStart);
2756 
2757   if (p.remainLen > kMatchSpecLenStart)
2758     p.remainLen = kMatchSpecLenStart;
2759 
2760   return SRes.OK;
2761 }
2762 
2763 alias ELzmaDummy = int;
2764 enum /*ELzmaDummy*/ {
2765   DUMMY_ERROR, /* unexpected end of input stream */
2766   DUMMY_LIT,
2767   DUMMY_MATCH,
2768   DUMMY_REP
2769 }
2770 
2771 private ELzmaDummy LzmaDec_TryDummy(const(CLzmaDec)* p, const(Byte)* buf, SizeT inSize)
2772 {
2773   UInt32 range = p.range;
2774   UInt32 code = p.code;
2775   const(Byte)* bufLimit = buf + inSize;
2776   const(CLzmaProb)* probs = p.probs;
2777   uint state = p.state;
2778   ELzmaDummy res;
2779 
2780   {
2781     const(CLzmaProb)* prob;
2782     UInt32 bound;
2783     uint ttt;
2784     uint posState = (p.processedPos) & ((1 << p.prop.pb) - 1);
2785 
2786     prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
2787     ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2788     {
2789       range = bound;
2790 
2791       /* if (bufLimit - buf >= 7) return DUMMY_LIT; */
2792 
2793       prob = probs + Literal;
2794       if (p.checkDicSize != 0 || p.processedPos != 0)
2795         prob += (cast(UInt32)LZMA_LIT_SIZE *
2796             ((((p.processedPos) & ((1 << (p.prop.lp)) - 1)) << p.prop.lc) +
2797             (p.dic[(p.dicPos == 0 ? p.dicBufSize : p.dicPos) - 1] >> (8 - p.prop.lc))));
2798 
2799       if (state < kNumLitStates)
2800       {
2801         uint symbol = 1;
2802         do { mixin(GET_BIT_CHECK!("prob + symbol", "symbol")); } while (symbol < 0x100);
2803       }
2804       else
2805       {
2806         uint matchByte = p.dic[p.dicPos - p.reps.ptr[0] +
2807             (p.dicPos < p.reps.ptr[0] ? p.dicBufSize : 0)];
2808         uint offs = 0x100;
2809         uint symbol = 1;
2810         do
2811         {
2812           uint bit;
2813           const(CLzmaProb)* probLit;
2814           matchByte <<= 1;
2815           bit = (matchByte & offs);
2816           probLit = prob + offs + bit + symbol;
2817           mixin(GET_BIT2_CHECK!("probLit", "symbol", "offs &= ~bit;", "offs &= bit;"));
2818         }
2819         while (symbol < 0x100);
2820       }
2821       res = DUMMY_LIT;
2822     }
2823     else
2824     {
2825       uint len;
2826       range -= bound; code -= bound;
2827 
2828       prob = probs + IsRep + state;
2829       ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2830       {
2831         range = bound;
2832         state = 0;
2833         prob = probs + LenCoder;
2834         res = DUMMY_MATCH;
2835       }
2836       else
2837       {
2838         range -= bound; code -= bound;
2839         res = DUMMY_REP;
2840         prob = probs + IsRepG0 + state;
2841         ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2842         {
2843           range = bound;
2844           prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
2845           ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2846           {
2847             range = bound;
2848             if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
2849             return DUMMY_REP;
2850           }
2851           else
2852           {
2853             range -= bound; code -= bound;
2854           }
2855         }
2856         else
2857         {
2858           range -= bound; code -= bound;
2859           prob = probs + IsRepG1 + state;
2860           ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2861           {
2862             range = bound;
2863           }
2864           else
2865           {
2866             range -= bound; code -= bound;
2867             prob = probs + IsRepG2 + state;
2868             ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2869             {
2870               range = bound;
2871             }
2872             else
2873             {
2874               range -= bound; code -= bound;
2875             }
2876           }
2877         }
2878         state = kNumStates;
2879         prob = probs + RepLenCoder;
2880       }
2881       {
2882         uint limit, offset;
2883         const(CLzmaProb)* probLen = prob + LenChoice;
2884         ttt = *(probLen); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2885         {
2886           range = bound;
2887           probLen = prob + LenLow + (posState << kLenNumLowBits);
2888           offset = 0;
2889           limit = 1 << kLenNumLowBits;
2890         }
2891         else
2892         {
2893           range -= bound; code -= bound;
2894           probLen = prob + LenChoice2;
2895           ttt = *(probLen); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
2896           {
2897             range = bound;
2898             probLen = prob + LenMid + (posState << kLenNumMidBits);
2899             offset = kLenNumLowSymbols;
2900             limit = 1 << kLenNumMidBits;
2901           }
2902           else
2903           {
2904             range -= bound; code -= bound;
2905             probLen = prob + LenHigh;
2906             offset = kLenNumLowSymbols + kLenNumMidSymbols;
2907             limit = 1 << kLenNumHighBits;
2908           }
2909         }
2910         mixin(TREE_DECODE_CHECK!("probLen", "limit", "len"));
2911         len += offset;
2912       }
2913 
2914       if (state < 4)
2915       {
2916         uint posSlot;
2917         prob = probs + PosSlot +
2918             ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
2919             kNumPosSlotBits);
2920         mixin(TREE_DECODE_CHECK!("prob", "1 << kNumPosSlotBits", "posSlot"));
2921         if (posSlot >= kStartPosModelIndex)
2922         {
2923           uint numDirectBits = ((posSlot >> 1) - 1);
2924 
2925           /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */
2926 
2927           if (posSlot < kEndPosModelIndex)
2928           {
2929             prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1;
2930           }
2931           else
2932           {
2933             numDirectBits -= kNumAlignBits;
2934             do
2935             {
2936               if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
2937               range >>= 1;
2938               code -= range & (((code - range) >> 31) - 1);
2939               /* if (code >= range) code -= range; */
2940             }
2941             while (--numDirectBits != 0);
2942             prob = probs + Align;
2943             numDirectBits = kNumAlignBits;
2944           }
2945           {
2946             uint i = 1;
2947             do
2948             {
2949               mixin(GET_BIT_CHECK!("prob + i", "i"));
2950             }
2951             while (--numDirectBits != 0);
2952           }
2953         }
2954       }
2955     }
2956   }
2957   if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
2958   return res;
2959 }
2960 
2961 
2962 void LzmaDec_InitDicAndState(CLzmaDec *p, bool initDic, bool initState)
2963 {
2964   p.needFlush = 1;
2965   p.remainLen = 0;
2966   p.tempBufSize = 0;
2967 
2968   if (initDic)
2969   {
2970     p.processedPos = 0;
2971     p.checkDicSize = 0;
2972     p.needInitState = 1;
2973   }
2974   if (initState)
2975     p.needInitState = 1;
2976 }
2977 
2978 public void LzmaDec_Init(CLzmaDec *p)
2979 {
2980   p.dicPos = 0;
2981   LzmaDec_InitDicAndState(p, true, true);
2982 }
2983 
2984 private void LzmaDec_InitStateReal(CLzmaDec *p)
2985 {
2986   SizeT numProbs = (Literal+(cast(UInt32)LZMA_LIT_SIZE<<((&p.prop).lc+(&p.prop).lp)));
2987   SizeT i;
2988   CLzmaProb *probs = p.probs;
2989   for (i = 0; i < numProbs; i++)
2990     probs[i] = kBitModelTotal >> 1;
2991   p.reps.ptr[0] = p.reps.ptr[1] = p.reps.ptr[2] = p.reps.ptr[3] = 1;
2992   p.state = 0;
2993   p.needInitState = 0;
2994 }
2995 
2996 public SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const(Byte)* src, SizeT *srcLen,
2997     ELzmaFinishMode finishMode, ELzmaStatus *status)
2998 {
2999   SizeT inSize = *srcLen;
3000   (*srcLen) = 0;
3001   LzmaDec_WriteRem(p, dicLimit);
3002 
3003   *status = LZMA_STATUS_NOT_SPECIFIED;
3004 
3005   while (p.remainLen != kMatchSpecLenStart)
3006   {
3007       int checkEndMarkNow;
3008 
3009       if (p.needFlush)
3010       {
3011         for (; inSize > 0 && p.tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
3012           p.tempBuf.ptr[p.tempBufSize++] = *src++;
3013         if (p.tempBufSize < RC_INIT_SIZE)
3014         {
3015           *status = LZMA_STATUS_NEEDS_MORE_INPUT;
3016           return SRes.OK;
3017         }
3018         if (p.tempBuf.ptr[0] != 0)
3019           return SRes.ERROR_DATA;
3020         p.code =
3021               (cast(UInt32)p.tempBuf.ptr[1] << 24)
3022             | (cast(UInt32)p.tempBuf.ptr[2] << 16)
3023             | (cast(UInt32)p.tempBuf.ptr[3] << 8)
3024             | (cast(UInt32)p.tempBuf.ptr[4]);
3025         p.range = 0xFFFFFFFF;
3026         p.needFlush = 0;
3027         p.tempBufSize = 0;
3028       }
3029 
3030       checkEndMarkNow = 0;
3031       if (p.dicPos >= dicLimit)
3032       {
3033         if (p.remainLen == 0 && p.code == 0)
3034         {
3035           *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
3036           return SRes.OK;
3037         }
3038         if (finishMode == LZMA_FINISH_ANY)
3039         {
3040           *status = LZMA_STATUS_NOT_FINISHED;
3041           return SRes.OK;
3042         }
3043         if (p.remainLen != 0)
3044         {
3045           *status = LZMA_STATUS_NOT_FINISHED;
3046           return SRes.ERROR_DATA;
3047         }
3048         checkEndMarkNow = 1;
3049       }
3050 
3051       if (p.needInitState)
3052         LzmaDec_InitStateReal(p);
3053 
3054       if (p.tempBufSize == 0)
3055       {
3056         SizeT processed;
3057         const(Byte)* bufLimit;
3058         if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
3059         {
3060           int dummyRes = LzmaDec_TryDummy(p, src, inSize);
3061           if (dummyRes == DUMMY_ERROR)
3062           {
3063             import core.stdc.string : memcpy;
3064             memcpy(p.tempBuf.ptr, src, inSize);
3065             p.tempBufSize = cast(uint)inSize;
3066             (*srcLen) += inSize;
3067             *status = LZMA_STATUS_NEEDS_MORE_INPUT;
3068             return SRes.OK;
3069           }
3070           if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
3071           {
3072             *status = LZMA_STATUS_NOT_FINISHED;
3073             return SRes.ERROR_DATA;
3074           }
3075           bufLimit = src;
3076         }
3077         else
3078           bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
3079         p.buf = src;
3080         if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0)
3081           return SRes.ERROR_DATA;
3082         processed = cast(SizeT)(p.buf - src);
3083         (*srcLen) += processed;
3084         src += processed;
3085         inSize -= processed;
3086       }
3087       else
3088       {
3089         uint rem = p.tempBufSize, lookAhead = 0;
3090         while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
3091           p.tempBuf.ptr[rem++] = src[lookAhead++];
3092         p.tempBufSize = rem;
3093         if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
3094         {
3095           int dummyRes = LzmaDec_TryDummy(p, p.tempBuf.ptr, rem);
3096           if (dummyRes == DUMMY_ERROR)
3097           {
3098             (*srcLen) += lookAhead;
3099             *status = LZMA_STATUS_NEEDS_MORE_INPUT;
3100             return SRes.OK;
3101           }
3102           if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
3103           {
3104             *status = LZMA_STATUS_NOT_FINISHED;
3105             return SRes.ERROR_DATA;
3106           }
3107         }
3108         p.buf = p.tempBuf.ptr;
3109         if (LzmaDec_DecodeReal2(p, dicLimit, p.buf) != 0)
3110           return SRes.ERROR_DATA;
3111 
3112         {
3113           uint kkk = cast(uint)(p.buf - p.tempBuf.ptr);
3114           if (rem < kkk)
3115             return SRes.ERROR_FAIL; /* some internal error */
3116           rem -= kkk;
3117           if (lookAhead < rem)
3118             return SRes.ERROR_FAIL; /* some internal error */
3119           lookAhead -= rem;
3120         }
3121         (*srcLen) += lookAhead;
3122         src += lookAhead;
3123         inSize -= lookAhead;
3124         p.tempBufSize = 0;
3125       }
3126   }
3127   if (p.code == 0)
3128     *status = LZMA_STATUS_FINISHED_WITH_MARK;
3129   return (p.code == 0) ? SRes.OK : SRes.ERROR_DATA;
3130 }
3131 
3132 public SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
3133 {
3134   import core.stdc.string : memcpy;
3135   SizeT outSize = *destLen;
3136   SizeT inSize = *srcLen;
3137   *srcLen = *destLen = 0;
3138   for (;;)
3139   {
3140     SizeT inSizeCur = inSize, outSizeCur, dicPos;
3141     ELzmaFinishMode curFinishMode;
3142     SRes res;
3143     if (p.dicPos == p.dicBufSize)
3144       p.dicPos = 0;
3145     dicPos = p.dicPos;
3146     if (outSize > p.dicBufSize - dicPos)
3147     {
3148       outSizeCur = p.dicBufSize;
3149       curFinishMode = LZMA_FINISH_ANY;
3150     }
3151     else
3152     {
3153       outSizeCur = dicPos + outSize;
3154       curFinishMode = finishMode;
3155     }
3156 
3157     res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
3158     src += inSizeCur;
3159     inSize -= inSizeCur;
3160     *srcLen += inSizeCur;
3161     outSizeCur = p.dicPos - dicPos;
3162     memcpy(dest, p.dic + dicPos, outSizeCur);
3163     dest += outSizeCur;
3164     outSize -= outSizeCur;
3165     *destLen += outSizeCur;
3166     if (res != 0)
3167       return res;
3168     if (outSizeCur == 0 || outSize == 0)
3169       return SRes.OK;
3170   }
3171 }
3172 
3173 public void LzmaDec_FreeProbs(CLzmaDec *p) {
3174   import core.stdc.stdlib : free;
3175   if (p.probs !is null) free(p.probs);
3176   p.probs = null;
3177 }
3178 
3179 private void LzmaDec_FreeDict(CLzmaDec *p) {
3180   import core.stdc.stdlib : free;
3181   if (p.dic !is null) free(p.dic);
3182   p.dic = null;
3183 }
3184 
3185 public void LzmaDec_Free(CLzmaDec *p) {
3186   LzmaDec_FreeProbs(p);
3187   LzmaDec_FreeDict(p);
3188 }
3189 
3190 public SRes LzmaProps_Decode(CLzmaProps *p, const(Byte)*data, uint size)
3191 {
3192   UInt32 dicSize;
3193   Byte d;
3194 
3195   if (size < LZMA_PROPS_SIZE)
3196     return SRes.ERROR_UNSUPPORTED;
3197   else
3198     dicSize = data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24);
3199 
3200   if (dicSize < LZMA_DIC_MIN)
3201     dicSize = LZMA_DIC_MIN;
3202   p.dicSize = dicSize;
3203 
3204   d = data[0];
3205   if (d >= (9 * 5 * 5))
3206     return SRes.ERROR_UNSUPPORTED;
3207 
3208   p.lc = d % 9;
3209   d /= 9;
3210   p.pb = d / 5;
3211   p.lp = d % 5;
3212 
3213   return SRes.OK;
3214 }
3215 
3216 private SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const(CLzmaProps)* propNew) {
3217   import core.stdc.stdlib : malloc;
3218   UInt32 numProbs = (Literal+(cast(UInt32)LZMA_LIT_SIZE<<((propNew).lc+(propNew).lp)));
3219   if (!p.probs || numProbs != p.numProbs)
3220   {
3221     LzmaDec_FreeProbs(p);
3222     p.probs = cast(CLzmaProb *)malloc(numProbs * CLzmaProb.sizeof);
3223     p.numProbs = numProbs;
3224     if (!p.probs)
3225       return SRes.ERROR_MEM;
3226   }
3227   return SRes.OK;
3228 }
3229 
3230 public SRes LzmaDec_AllocateProbs(CLzmaDec *p, const(Byte)* props, uint propsSize)
3231 {
3232   CLzmaProps propNew;
3233   if (auto sres = LzmaProps_Decode(&propNew, props, propsSize)) return sres;
3234   if (auto sres = LzmaDec_AllocateProbs2(p, &propNew)) return sres;
3235   p.prop = propNew;
3236   return SRes.OK;
3237 }
3238 
3239 public SRes LzmaDec_Allocate(CLzmaDec *p, const(Byte)*props, uint propsSize)
3240 {
3241   import core.stdc.stdlib : malloc;
3242   CLzmaProps propNew;
3243   SizeT dicBufSize;
3244   if (auto sres = LzmaProps_Decode(&propNew, props, propsSize)) return sres;
3245   if (auto sres = LzmaDec_AllocateProbs2(p, &propNew)) return sres;
3246 
3247   {
3248     UInt32 dictSize = propNew.dicSize;
3249     SizeT mask = (1U << 12) - 1;
3250          if (dictSize >= (1U << 30)) mask = (1U << 22) - 1;
3251     else if (dictSize >= (1U << 22)) mask = (1U << 20) - 1;
3252     dicBufSize = (cast(SizeT)dictSize + mask) & ~mask;
3253     if (dicBufSize < dictSize)
3254       dicBufSize = dictSize;
3255   }
3256 
3257   if (!p.dic || dicBufSize != p.dicBufSize)
3258   {
3259     LzmaDec_FreeDict(p);
3260     p.dic = cast(Byte *)malloc(dicBufSize);
3261     if (!p.dic)
3262     {
3263       LzmaDec_FreeProbs(p);
3264       return SRes.ERROR_MEM;
3265     }
3266   }
3267   p.dicBufSize = dicBufSize;
3268   p.prop = propNew;
3269   return SRes.OK;
3270 }
3271 
3272 public SRes LzmaDecode(Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen,
3273     const(Byte)* propData, uint propSize, ELzmaFinishMode finishMode,
3274     ELzmaStatus *status)
3275 {
3276   CLzmaDec p;
3277   SRes res;
3278   SizeT outSize = *destLen, inSize = *srcLen;
3279   *destLen = *srcLen = 0;
3280   *status = LZMA_STATUS_NOT_SPECIFIED;
3281   if (inSize < RC_INIT_SIZE)
3282     return SRes.ERROR_INPUT_EOF;
3283   //LzmaDec_Construct(&p);
3284   p.dic = null; p.probs = null;
3285   if (auto sres = LzmaDec_AllocateProbs(&p, propData, propSize)) return sres;
3286   p.dic = dest;
3287   p.dicBufSize = outSize;
3288   LzmaDec_Init(&p);
3289   *srcLen = inSize;
3290   res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);
3291   *destLen = p.dicPos;
3292   if (res == SRes.OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
3293     res = SRes.ERROR_INPUT_EOF;
3294   LzmaDec_FreeProbs(&p);
3295   return res;
3296 }
3297 
3298 
3299 
3300 /* Lzma2Dec.c -- LZMA2 Decoder
3301 2009-05-03 : Igor Pavlov : Public domain */
3302 // also by Lasse Collin
3303 // ported to D by adr.
3304 
3305 /*
3306 00000000  -  EOS
3307 00000001 U U  -  Uncompressed Reset Dic
3308 00000010 U U  -  Uncompressed No Reset
3309 100uuuuu U U P P  -  LZMA no reset
3310 101uuuuu U U P P  -  LZMA reset state
3311 110uuuuu U U P P S  -  LZMA reset state + new prop
3312 111uuuuu U U P P S  -  LZMA reset state + new prop + reset dic
3313 
3314   u, U - Unpack Size
3315   P - Pack Size
3316   S - Props
3317 */
3318 
3319 struct CLzma2Dec
3320 {
3321   CLzmaDec decoder;
3322   UInt32 packSize;
3323   UInt32 unpackSize;
3324   int state;
3325   Byte control;
3326   bool needInitDic;
3327   bool needInitState;
3328   bool needInitProp;
3329 }
3330 
3331 enum LZMA2_CONTROL_LZMA = (1 << 7);
3332 enum LZMA2_CONTROL_COPY_NO_RESET = 2;
3333 enum LZMA2_CONTROL_COPY_RESET_DIC = 1;
3334 enum LZMA2_CONTROL_EOF = 0;
3335 
3336 auto LZMA2_IS_UNCOMPRESSED_STATE(P)(P p) { return (((p).control & LZMA2_CONTROL_LZMA) == 0); }
3337 
3338 auto LZMA2_GET_LZMA_MODE(P)(P p) { return (((p).control >> 5) & 3); }
3339 auto LZMA2_IS_THERE_PROP(P)(P mode) { return ((mode) >= 2); }
3340 
3341 enum LZMA2_LCLP_MAX = 4;
3342 auto LZMA2_DIC_SIZE_FROM_PROP(P)(P p) { return ((cast(UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)); }
3343 
3344 enum ELzma2State
3345 {
3346   LZMA2_STATE_CONTROL,
3347   LZMA2_STATE_UNPACK0,
3348   LZMA2_STATE_UNPACK1,
3349   LZMA2_STATE_PACK0,
3350   LZMA2_STATE_PACK1,
3351   LZMA2_STATE_PROP,
3352   LZMA2_STATE_DATA,
3353   LZMA2_STATE_DATA_CONT,
3354   LZMA2_STATE_FINISHED,
3355   LZMA2_STATE_ERROR
3356 }
3357 
3358 static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props)
3359 {
3360   UInt32 dicSize;
3361   if (prop > 40)
3362     return SRes.ERROR_UNSUPPORTED;
3363   dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop);
3364   props[0] = cast(Byte)LZMA2_LCLP_MAX;
3365   props[1] = cast(Byte)(dicSize);
3366   props[2] = cast(Byte)(dicSize >> 8);
3367   props[3] = cast(Byte)(dicSize >> 16);
3368   props[4] = cast(Byte)(dicSize >> 24);
3369   return SRes.OK;
3370 }
3371 
3372 SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop)
3373 {
3374   Byte[LZMA_PROPS_SIZE] props;
3375   auto wtf = Lzma2Dec_GetOldProps(prop, props.ptr);
3376   if(wtf != 0) return wtf;
3377   return LzmaDec_AllocateProbs(&p.decoder, props.ptr, LZMA_PROPS_SIZE);
3378 }
3379 
3380 SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop)
3381 {
3382   Byte[LZMA_PROPS_SIZE] props;
3383   auto wtf = Lzma2Dec_GetOldProps(prop, props.ptr);
3384   if(wtf != 0) return wtf;
3385   return LzmaDec_Allocate(&p.decoder, props.ptr, LZMA_PROPS_SIZE);
3386 }
3387 
3388 void Lzma2Dec_Init(CLzma2Dec *p)
3389 {
3390   p.state = ELzma2State.LZMA2_STATE_CONTROL;
3391   p.needInitDic = true;
3392   p.needInitState = true;
3393   p.needInitProp = true;
3394   LzmaDec_Init(&p.decoder);
3395 }
3396 
3397 static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b)
3398 {
3399   switch(p.state)
3400   {
3401     default: return ELzma2State.LZMA2_STATE_ERROR;
3402     case ELzma2State.LZMA2_STATE_CONTROL:
3403       p.control = b;
3404       if (p.control == 0)
3405         return ELzma2State.LZMA2_STATE_FINISHED;
3406       if (LZMA2_IS_UNCOMPRESSED_STATE(p))
3407       {
3408         if ((p.control & 0x7F) > 2)
3409           return ELzma2State.LZMA2_STATE_ERROR;
3410         p.unpackSize = 0;
3411       }
3412       else
3413         p.unpackSize = cast(UInt32)(p.control & 0x1F) << 16;
3414       return ELzma2State.LZMA2_STATE_UNPACK0;
3415 
3416     case ELzma2State.LZMA2_STATE_UNPACK0:
3417       p.unpackSize |= cast(UInt32)b << 8;
3418       return ELzma2State.LZMA2_STATE_UNPACK1;
3419 
3420     case ELzma2State.LZMA2_STATE_UNPACK1:
3421       p.unpackSize |= cast(UInt32)b;
3422       p.unpackSize++;
3423       return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? ELzma2State.LZMA2_STATE_DATA : ELzma2State.LZMA2_STATE_PACK0;
3424 
3425     case ELzma2State.LZMA2_STATE_PACK0:
3426       p.packSize = cast(UInt32)b << 8;
3427       return ELzma2State.LZMA2_STATE_PACK1;
3428 
3429     case ELzma2State.LZMA2_STATE_PACK1:
3430       p.packSize |= cast(UInt32)b;
3431       p.packSize++;
3432       return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? ELzma2State.LZMA2_STATE_PROP:
3433         (p.needInitProp ? ELzma2State.LZMA2_STATE_ERROR : ELzma2State.LZMA2_STATE_DATA);
3434 
3435     case ELzma2State.LZMA2_STATE_PROP:
3436     {
3437       int lc, lp;
3438       if (b >= (9 * 5 * 5))
3439         return ELzma2State.LZMA2_STATE_ERROR;
3440       lc = b % 9;
3441       b /= 9;
3442       p.decoder.prop.pb = b / 5;
3443       lp = b % 5;
3444       if (lc + lp > LZMA2_LCLP_MAX)
3445         return ELzma2State.LZMA2_STATE_ERROR;
3446       p.decoder.prop.lc = lc;
3447       p.decoder.prop.lp = lp;
3448       p.needInitProp = false;
3449       return ELzma2State.LZMA2_STATE_DATA;
3450     }
3451   }
3452 }
3453 
3454 static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const(Byte) *src, SizeT size)
3455 {
3456   import core.stdc.string;
3457   memcpy(p.dic + p.dicPos, src, size);
3458   p.dicPos += size;
3459   if (p.checkDicSize == 0 && p.prop.dicSize - p.processedPos <= size)
3460     p.checkDicSize = p.prop.dicSize;
3461   p.processedPos += cast(UInt32)size;
3462 }
3463 
3464 SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit,
3465     const(Byte) *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
3466 {
3467   SizeT inSize = *srcLen;
3468   *srcLen = 0;
3469   *status = LZMA_STATUS_NOT_SPECIFIED;
3470 
3471   while (p.state != ELzma2State.LZMA2_STATE_FINISHED)
3472   {
3473     SizeT dicPos = p.decoder.dicPos;
3474     if (p.state == ELzma2State.LZMA2_STATE_ERROR)
3475       return SRes.ERROR_DATA;
3476     if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY)
3477     {
3478       *status = LZMA_STATUS_NOT_FINISHED;
3479       return SRes.OK;
3480     }
3481     if (p.state != ELzma2State.LZMA2_STATE_DATA && p.state != ELzma2State.LZMA2_STATE_DATA_CONT)
3482     {
3483       if (*srcLen == inSize)
3484       {
3485         *status = LZMA_STATUS_NEEDS_MORE_INPUT;
3486         return SRes.OK;
3487       }
3488       (*srcLen)++;
3489       p.state = Lzma2Dec_UpdateState(p, *src++);
3490       continue;
3491     }
3492     {
3493       SizeT destSizeCur = dicLimit - dicPos;
3494       SizeT srcSizeCur = inSize - *srcLen;
3495       ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY;
3496 
3497       if (p.unpackSize <= destSizeCur)
3498       {
3499         destSizeCur = cast(SizeT)p.unpackSize;
3500         curFinishMode = LZMA_FINISH_END;
3501       }
3502 
3503       if (LZMA2_IS_UNCOMPRESSED_STATE(p))
3504       {
3505         if (*srcLen == inSize)
3506         {
3507           *status = LZMA_STATUS_NEEDS_MORE_INPUT;
3508           return SRes.OK;
3509         }
3510 
3511         if (p.state == ELzma2State.LZMA2_STATE_DATA)
3512         {
3513           bool initDic = (p.control == LZMA2_CONTROL_COPY_RESET_DIC);
3514           if (initDic)
3515             p.needInitProp = p.needInitState = true;
3516           else if (p.needInitDic)
3517             return SRes.ERROR_DATA;
3518           p.needInitDic = false;
3519           LzmaDec_InitDicAndState(&p.decoder, initDic, false);
3520         }
3521 
3522         if (srcSizeCur > destSizeCur)
3523           srcSizeCur = destSizeCur;
3524 
3525         if (srcSizeCur == 0)
3526           return SRes.ERROR_DATA;
3527 
3528         LzmaDec_UpdateWithUncompressed(&p.decoder, src, srcSizeCur);
3529 
3530         src += srcSizeCur;
3531         *srcLen += srcSizeCur;
3532         p.unpackSize -= cast(UInt32)srcSizeCur;
3533         p.state = (p.unpackSize == 0) ? ELzma2State.LZMA2_STATE_CONTROL : ELzma2State.LZMA2_STATE_DATA_CONT;
3534       }
3535       else
3536       {
3537         SizeT outSizeProcessed;
3538         SRes res;
3539 
3540         if (p.state == ELzma2State.LZMA2_STATE_DATA)
3541         {
3542           int mode = LZMA2_GET_LZMA_MODE(p);
3543           bool initDic = (mode == 3);
3544           bool initState = (mode > 0);
3545           if ((!initDic && p.needInitDic) || (!initState && p.needInitState))
3546             return SRes.ERROR_DATA;
3547 
3548           LzmaDec_InitDicAndState(&p.decoder, initDic, initState);
3549           p.needInitDic = false;
3550           p.needInitState = false;
3551           p.state = ELzma2State.LZMA2_STATE_DATA_CONT;
3552         }
3553         if (srcSizeCur > p.packSize)
3554           srcSizeCur = cast(SizeT)p.packSize;
3555 
3556         res = LzmaDec_DecodeToDic(&p.decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status);
3557 
3558         src += srcSizeCur;
3559         *srcLen += srcSizeCur;
3560         p.packSize -= cast(UInt32)srcSizeCur;
3561 
3562         outSizeProcessed = p.decoder.dicPos - dicPos;
3563         p.unpackSize -= cast(UInt32)outSizeProcessed;
3564 
3565         if(res != 0) return res;
3566         if (*status == LZMA_STATUS_NEEDS_MORE_INPUT)
3567           return res;
3568 
3569         if (srcSizeCur == 0 && outSizeProcessed == 0)
3570         {
3571           if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ||
3572               p.unpackSize != 0 || p.packSize != 0)
3573             return SRes.ERROR_DATA;
3574           p.state = ELzma2State.LZMA2_STATE_CONTROL;
3575         }
3576         if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
3577           *status = LZMA_STATUS_NOT_FINISHED;
3578       }
3579     }
3580   }
3581   *status = LZMA_STATUS_FINISHED_WITH_MARK;
3582   return SRes.OK;
3583 }
3584 
3585 SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const(Byte) *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
3586 {
3587   import core.stdc.string;
3588   SizeT outSize = *destLen, inSize = *srcLen;
3589   *srcLen = *destLen = 0;
3590   for (;;)
3591   {
3592     SizeT srcSizeCur = inSize, outSizeCur, dicPos;
3593     ELzmaFinishMode curFinishMode;
3594     SRes res;
3595     if (p.decoder.dicPos == p.decoder.dicBufSize)
3596       p.decoder.dicPos = 0;
3597     dicPos = p.decoder.dicPos;
3598     if (outSize > p.decoder.dicBufSize - dicPos)
3599     {
3600       outSizeCur = p.decoder.dicBufSize;
3601       curFinishMode = LZMA_FINISH_ANY;
3602     }
3603     else
3604     {
3605       outSizeCur = dicPos + outSize;
3606       curFinishMode = finishMode;
3607     }
3608 
3609     res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status);
3610     src += srcSizeCur;
3611     inSize -= srcSizeCur;
3612     *srcLen += srcSizeCur;
3613     outSizeCur = p.decoder.dicPos - dicPos;
3614     memcpy(dest, p.decoder.dic + dicPos, outSizeCur);
3615     dest += outSizeCur;
3616     outSize -= outSizeCur;
3617     *destLen += outSizeCur;
3618     if (res != 0)
3619       return res;
3620     if (outSizeCur == 0 || outSize == 0)
3621       return SRes.OK;
3622   }
3623 }
3624 
3625 SRes Lzma2Decode(Byte *dest, SizeT *destLen, const(Byte) *src, SizeT *srcLen,
3626     Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status)
3627 {
3628   CLzma2Dec decoder;
3629   SRes res;
3630   SizeT outSize = *destLen, inSize = *srcLen;
3631   Byte[LZMA_PROPS_SIZE] props;
3632 
3633   //Lzma2Dec_Construct(&decoder);
3634 
3635   *destLen = *srcLen = 0;
3636   *status = LZMA_STATUS_NOT_SPECIFIED;
3637   decoder.decoder.dic = dest;
3638   decoder.decoder.dicBufSize = outSize;
3639 
3640   auto wtf = Lzma2Dec_GetOldProps(prop, props.ptr);
3641   if(wtf != 0) return wtf;
3642   wtf = LzmaDec_AllocateProbs(&decoder.decoder, props.ptr, LZMA_PROPS_SIZE);
3643   if(wtf != 0) return wtf;
3644 
3645   *srcLen = inSize;
3646   res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status);
3647   *destLen = decoder.decoder.dicPos;
3648   if (res == SRes.OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
3649     res = SRes.ERROR_INPUT_EOF;
3650 
3651   LzmaDec_FreeProbs(&decoder.decoder);
3652   return res;
3653 }
3654 
3655 }