Browse Source

Avoid allocation, remove code duplication for decompressing zlib data

pull/2110/head
Brian Popow 4 years ago
parent
commit
2dd35982e7
  1. 77
      src/ImageSharp/Formats/Png/PngDecoderCore.cs

77
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -1206,27 +1206,45 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<byte> compressedData = data.Slice(zeroIndex + 2); ReadOnlySpan<byte> compressedData = data.Slice(zeroIndex + 2);
using (var memoryStream = new MemoryStream(compressedData.ToArray())) if (this.TryUncompressZlibData(compressedData, out byte[] iccpProfileBytes))
using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStream))
using (var inflateStream = new ZlibInflateStream(bufferedStream))
{ {
if (!inflateStream.AllocateNewBytes(compressedData.Length, false)) metadata.IccProfile = new IccProfile(iccpProfileBytes);
}
}
/// <summary>
/// Tries to un-compress zlib compressed data.
/// </summary>
/// <param name="compressedData">The compressed data.</param>
/// <param name="uncompressedBytesArray">The uncompressed bytes array.</param>
/// <returns>True, if de-compressing was successful.</returns>
private unsafe bool TryUncompressZlibData(ReadOnlySpan<byte> compressedData, out byte[] uncompressedBytesArray)
{
fixed (byte* compressedDataBase = compressedData)
{
using (var memoryStream = new UnmanagedMemoryStream(compressedDataBase, compressedData.Length))
using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStream))
using (var inflateStream = new ZlibInflateStream(bufferedStream))
{ {
return; if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
} {
uncompressedBytesArray = Array.Empty<byte>();
return false;
}
var uncompressedBytes = new List<byte>(); var uncompressedBytes = new List<byte>(compressedData.Length);
// Note: this uses a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here. // Note: this uses a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length); int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
while (bytesRead != 0) while (bytesRead != 0)
{ {
uncompressedBytes.AddRange(this.buffer.AsSpan(0, bytesRead).ToArray()); uncompressedBytes.AddRange(this.buffer.AsSpan(0, bytesRead).ToArray());
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length); bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
} }
byte[] iccpProfileBytes = uncompressedBytes.ToArray(); uncompressedBytesArray = uncompressedBytes.ToArray();
metadata.IccProfile = new IccProfile(iccpProfileBytes); return true;
}
} }
} }
@ -1362,7 +1380,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
else if (this.IsXmpTextData(keywordBytes)) else if (this.IsXmpTextData(keywordBytes))
{ {
XmpProfile xmpProfile = new XmpProfile(data.Slice(dataStartIdx).ToArray()); var xmpProfile = new XmpProfile(data.Slice(dataStartIdx).ToArray());
metadata.XmpProfile = xmpProfile; metadata.XmpProfile = xmpProfile;
} }
else else
@ -1381,29 +1399,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>The <see cref="bool"/>.</returns> /// <returns>The <see cref="bool"/>.</returns>
private bool TryUncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding, out string value) private bool TryUncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding, out string value)
{ {
using (var memoryStream = new MemoryStream(compressedData.ToArray())) if (this.TryUncompressZlibData(compressedData, out byte[] uncompressedData))
using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStream))
using (var inflateStream = new ZlibInflateStream(bufferedStream))
{ {
if (!inflateStream.AllocateNewBytes(compressedData.Length, false)) value = encoding.GetString(uncompressedData);
{
value = null;
return false;
}
var uncompressedBytes = new List<byte>();
// Note: this uses a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
while (bytesRead != 0)
{
uncompressedBytes.AddRange(this.buffer.AsSpan(0, bytesRead).ToArray());
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
}
value = encoding.GetString(uncompressedBytes.ToArray());
return true; return true;
} }
value = null;
return false;
} }
/// <summary> /// <summary>

Loading…
Cancel
Save