/// <summary>Transforms a stream.</summary> /// <param name="input">The input <see cref="Stream"/> containing the data to compress or decompress</param> /// <param name="output">The output <see cref="Stream"/> where the results should be written</param> /// <param name="flush">The flush mode to use. The default is <see cref="ZlibFlush.Finish"/>, which means that /// <paramref name="input"/> represents the entire remaining data to transform. /// </param> public unsafe void Transform(Stream input, Stream output, ZlibFlush flush = ZlibFlush.Finish) { if (input == null) { throw new ArgumentNullException(nameof(input)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } AssertUsable(); byte[] inbuf = ArrayPool <byte> .Shared.Rent(4096), outbuf = ArrayPool <byte> .Shared.Rent(4096); bool more; while (true) { int read = input.Read(inbuf, 0, inbuf.Length); // read some data from the input if (read == 0) // if there was no more data... { if (Mode == CompressionMode.Decompress && flush == ZlibFlush.Finish) { throw new EndOfStreamException(); } break; } for (int index = 0; index < read;) // transform the input we read { more = Transform(inbuf, index, read - index, out int consumed, outbuf, 0, outbuf.Length, out int produced, ZlibFlush.None); // transform some of the input output.Write(outbuf, 0, produced); index += consumed; if (!more) // if no more data is needed (e.g. because we reached the end of the decompressed data) { if (input.CanSeek) { input.Seek(index - read, SeekOrigin.Current); // try to put back the extra input we read } return; // and we're done } } } while (true) // at this point, we've transformed all of the input, but we need to flush the remaining output { more = Transform(inbuf, 0, 0, out int _, outbuf, 0, outbuf.Length, out int produced, flush); output.Write(outbuf, 0, produced); if (!more || produced == 0) { break; // if we've reached the end while decompressing or if no data was produced, we're done } } }
public unsafe byte[] Transform(byte[] input, int index, int length, ZlibFlush flush = ZlibFlush.Finish) #endif { Utility.ValidateRange(input, index, length); byte[] buffer = new byte[Math.Max(64, Mode == CompressionMode.Compress ? input.Length / 4 : input.Length * 3 / 2)]; int outputLength = 0; while (true) { bool more = Transform(input, index, length, out int read, buffer, outputLength, buffer.Length - outputLength, out int written, flush); outputLength += written; index += read; length -= read; if (!more) // if we transformed everything (when flush is Finish) { // if we were decompressing and reached the end of compressed data before the input was exhausted, complain if (length != 0) { throw new InvalidDataException("Too much data was provided."); } break; // otherwise, we're done } else if (outputLength == buffer.Length) // if the buffer is full, maybe we need to provide more output space { Array.Resize(ref buffer, buffer.Length + Math.Max(64, Mode == CompressionMode.Compress ? length : length * 3 / 2)); } else if (length == 0) // otherwise, we've given all we got, but zlib still wants more { if (flush == ZlibFlush.Finish) { throw new EndOfStreamException(); // if they didn't provide enough data, throw } break; // otherwise, the user should call us again with more input } } #if !NETSTANDARD2_0 && !NET45 && !NET46 return(new Memory <byte>(buffer, 0, outputLength)); #else Array.Resize(ref buffer, outputLength); return(buffer); #endif }
/// <summary>Transforms a single chunks of data.</summary> /// <param name="input">The array containing the data to compress or decompress</param> /// <param name="inputIndex">The index within <paramref name="input"/> where the data to transform begins</param> /// <param name="inputLength">The number of bytes within <paramref name="input"/> to transform</param> /// <param name="bytesRead">A variable that will receive the number of bytes read from the input</param> /// <param name="output">The array where the compressed or decompressed data should be written</param> /// <param name="outputIndex">The index within <paramref name="output"/> where the transformed data should be written</param> /// <param name="outputLength">The available space within <paramref name="output"/> for transformed data to be written</param> /// <param name="bytesWritten">A variable that will receive the number of bytes written to the output</param> /// <param name="flush">The <see cref="ZlibFlush"/> mode of the transformation</param> /// <returns>Returns false if the transformation is complete (when <paramref name="flush"/> is <see cref="ZlibFlush.Finish"/>), /// and true otherwise. /// </returns> public unsafe bool Transform(byte[] input, int inputIndex, int inputLength, out int bytesRead, byte[] output, int outputIndex, int outputLength, out int bytesWritten, ZlibFlush flush) { Utility.ValidateRange(input, inputIndex, inputLength); Utility.ValidateRange(output, outputIndex, outputLength); AssertUsable(); used = true; codec.InputBuffer = input; codec.NextIn = inputIndex; codec.AvailableBytesIn = inputLength; codec.OutputBuffer = output; codec.NextOut = outputIndex; codec.AvailableBytesOut = outputLength; retry: var res = (Result)(Mode == CompressionMode.Compress ? codec.Deflate((Ionic.Zlib.FlushType)flush) : codec.Inflate((Ionic.Zlib.FlushType)flush)); if (res == Result.NeedDict) // if we're decompressing and need the initial dictionary... { CheckResult((Result)codec.SetDictionary(dictionary)); // provide it if (codec.AvailableBytesIn != 0 && codec.AvailableBytesOut != 0) { goto retry; // if more space is available, continue } } bytesRead = codec.NextIn - inputIndex; bytesWritten = codec.NextOut - outputIndex; return(res != Result.EOF); }
/// <summary>Transforms a region of memory.</summary> /// <param name="input">The array containing the data to compress or decompress</param> /// <param name="index">The index within <paramref name="input"/> where the data begins</param> /// <param name="length">The number of bytes to transform</param> /// <param name="flush">The flush mode to use. The default is <see cref="ZlibFlush.Finish"/>, which means that /// <paramref name="input"/> represents the entire remaining data to transform. /// </param> /// <remarks>It is not efficient to use this method to transform a large amount of data.</remarks> #if !NETSTANDARD2_0 && !NET45 && !NET46 public unsafe Memory <byte> Transform(byte[] input, int index, int length, ZlibFlush flush = ZlibFlush.Finish)
public byte[] Transform(byte[] input, ZlibFlush flush = ZlibFlush.Finish) => Transform(input, 0, input.Length, flush);