From b58825345b901588fdf4ce48ff2acb69d1fb72b7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 5 Mar 2021 14:00:09 +0100 Subject: [PATCH] Use BinaryPrimitives instead of BitConverter and scratch buffer to avoid allocations --- .../Formats/Tiff/TiffEncoderCore.cs | 17 +++++--- .../Formats/Tiff/Writers/TiffStreamWriter.cs | 40 ++++++++++++++----- .../Formats/Tiff/Utils/TiffWriterTests.cs | 5 ++- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 300814421..52a367645 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -36,6 +36,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// private readonly MemoryAllocator memoryAllocator; + /// + /// A scratch buffer to reduce allocations. + /// + private readonly byte[] buffer = new byte[4]; + /// /// The global configuration. /// @@ -238,15 +243,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff writer.Write(ExifWriter.GetNumberOfComponents(entry)); uint length = ExifWriter.GetLength(entry); - var raw = new byte[length]; - int sz = ExifWriter.WriteValue(entry, raw, 0); - DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); - if (raw.Length <= 4) + if (length <= 4) { - writer.WritePadded(raw); + int sz = ExifWriter.WriteValue(entry, this.buffer, 0); + DebugGuard.IsTrue(sz == length, "Incorrect number of bytes written"); + writer.WritePadded(this.buffer.AsSpan(0, sz)); } else { + var raw = new byte[length]; + int sz = ExifWriter.WriteValue(entry, raw, 0); + DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); largeDataBlocks.Add(raw); writer.Write(dataOffset); dataOffset += (uint)(raw.Length + (raw.Length % 2)); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 39d46c878..7a49d4b82 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; using System.IO; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers @@ -13,6 +14,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { private static readonly byte[] PaddingBytes = new byte[4]; + /// + /// A scratch buffer to reduce allocations. + /// + private readonly byte[] buffer = new byte[4]; + /// /// Initializes a new instance of the class. /// @@ -37,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// /// Writes an empty four bytes to the stream, returning the offset to be written later. /// - /// The offset to be written later + /// The offset to be written later. public long PlaceMarker() { long offset = this.BaseStream.Position; @@ -69,8 +75,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The two-byte unsigned integer to write. public void Write(ushort value) { - byte[] bytes = BitConverter.GetBytes(value); - this.BaseStream.Write(bytes, 0, 2); + if (this.IsLittleEndian) + { + BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value); + } + else + { + BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value); + } + + this.BaseStream.Write(this.buffer.AsSpan(0, 2)); } /// @@ -79,21 +93,29 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The four-byte unsigned integer to write. public void Write(uint value) { - byte[] bytes = BitConverter.GetBytes(value); - this.BaseStream.Write(bytes, 0, 4); + if (this.IsLittleEndian) + { + BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); + } + else + { + BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value); + } + + this.BaseStream.Write(this.buffer.AsSpan(0, 4)); } /// /// Writes an array of bytes to the current stream, padded to four-bytes. /// /// The bytes to write. - public void WritePadded(byte[] value) + public void WritePadded(Span value) { - this.BaseStream.Write(value, 0, value.Length); + this.BaseStream.Write(value); - if (value.Length < 4) + if (value.Length % 4 != 0) { - this.BaseStream.Write(PaddingBytes, 0, 4 - value.Length); + this.BaseStream.Write(PaddingBytes, 0, 4 - (value.Length % 4)); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 7ea2e4cc4..b1389cec5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -76,12 +76,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils } [Theory] - [InlineData(new byte[] { }, new byte[] { 0, 0, 0, 0 })] + [InlineData(new byte[] { }, new byte[] { })] [InlineData(new byte[] { 2 }, new byte[] { 2, 0, 0, 0 })] [InlineData(new byte[] { 2, 4 }, new byte[] { 2, 4, 0, 0 })] [InlineData(new byte[] { 2, 4, 6 }, new byte[] { 2, 4, 6, 0 })] [InlineData(new byte[] { 2, 4, 6, 8 }, new byte[] { 2, 4, 6, 8 })] - [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12 })] + [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12, 0, 0 })] + public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) { using var stream = new MemoryStream();