XzDecoder.processData

Continues an in-progress decompression of the src data, putting it into the dest buffer. The src data must be at least 20 bytes long, but I'd recommend making it at larger.

Returns the slice of the head of the dest buffer actually filled, then updates the following member variables of XzDecoder:

  • finished will be true if the compressed data has been completely decompressed.
  • 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.
  • And very importantly, unprocessed will contain a slice of the 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.

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.

If your src buffer contains the entire compressed file, you can pass unprocessed in a loop until finished:

static import std.file;
auto compressedData = cast(immutable(ubyte)[]) std.file.read("file.xz"); // load it all into memory at once
XzDecoder decoder = XzDecoder(compressedData);
auto buffer = new ubyte[](4096); // to hold chunks of uncompressed data
ubyte[] wholeUncompressedFile;
while(!decoder.finished) {
	// it returns the slice of buffer with new data, so we can append that
	// to reconstruct the whole file. and then it sets `decoded.unprocessed` to
	// a slice of what is left out of the source file to continue processing in
	// the next iteration of the loop.
	wholeUncompressedFile ~= decoder.processData(buffer, decoder.unprocessed);
}
// wholeUncompressedFile is now fully populated

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.

import std.stdio;
auto file = File("file.xz");
ubyte[] compressedDataBuffer = new ubyte[](1024 * 32);

// read the first chunk. all modifications will be done through `compressedDataBuffer`
// so we can make this part const for easier assignment to the slices `decoder.unprocessed` will hold
const(ubyte)[] compressedData = file.rawRead(compressedDataBuffer);

XzDecoder decoder = XzDecoder(compressedData);

// need to keep what was unprocessed after construction
compressedData = decoder.unprocessed;

auto buffer = new ubyte[](4096); // to hold chunks of uncompressed data
ubyte[] wholeUncompressedFile;
while(!decoder.finished) {
	wholeUncompressedFile ~= decoder.processData(buffer, compressedData);

	if(decoder.needsMoreData) {
		// it needed more data to fill the buffer

		// first, move the unprocessed data bask to the head
		// you cannot necessarily use a D slice assign operator
		// because the `unprocessed` is a slice of the `compressedDataBuffer`,
		// meaning they might overlap. Instead, we'll use C's `memmove`
		import core.stdc.string;
		memmove(compressedDataBuffer.ptr, decoder.unprocessed.ptr, decoder.unprocessed.length);


		// now we can read more data to fill in the tail of the buffer again
		auto newlyRead = file.rawRead(compressedDataBuffer[decoder.unprocessed.length .. $]);

		// the new compressed data ready to process is what we moved from before,
		// now at the head of the buffer, plus what was just read, at the end of
		// the same buffer
		compressedData = compressedDataBuffer[0 .. decoder.unprocessed.length + newlyRead.length];
	} else {
		// otherwise, the output buffer was full, but there's probably
		// still more unprocessed data. Set it to be used on the next
		// loop iteration.
		compressedData = decoder.unprocessed;
	}
}
// wholeUncompressedFile is now fully populated
struct XzDecoder
ubyte[]
processData
(
ubyte[] dest
,
const(ubyte)[] src
)

Meta