From affc1ed8b5b7416dff7bca9e4658e0f28ac98f6d Mon Sep 17 00:00:00 2001 From: BobLd <38405645+BobLd@users.noreply.github.com> Date: Sat, 8 Jun 2024 06:16:09 +0100 Subject: [PATCH] Seal and update IFilters to return ReadOnlyMemory (#843) * Avoid ToArray() in memoryFactory * Seal and update IFilters to return ReadOnlyMemory * Fix filter tests * Seal and update IFilters to return ReadOnlyMemory --- .../Filters/Ascii85FilterTests.cs | 8 ++++---- .../Filters/AsciiHexDecodeFilterTests.cs | 12 ++++++------ .../Filters/CcittFaxDecodeFilterTests.cs | 2 +- .../Filters/FlateFilterTests.cs | 2 +- .../Filters/RunLengthFilterTests.cs | 4 ++-- .../Integration/PdfParserTests.cs | 4 ++-- src/UglyToad.PdfPig/Content/InlineImage.cs | 6 +++--- src/UglyToad.PdfPig/Filters/Ascii85Filter.cs | 8 +++----- src/UglyToad.PdfPig/Filters/AsciiHexDecodeFilter.cs | 4 ++-- src/UglyToad.PdfPig/Filters/CcittFaxDecodeFilter.cs | 2 +- src/UglyToad.PdfPig/Filters/DctDecodeFilter.cs | 4 ++-- src/UglyToad.PdfPig/Filters/FlateFilter.cs | 8 +++----- src/UglyToad.PdfPig/Filters/IFilter.cs | 2 +- src/UglyToad.PdfPig/Filters/Jbig2DecodeFilter.cs | 2 +- src/UglyToad.PdfPig/Filters/JpxDecodeFilter.cs | 4 ++-- src/UglyToad.PdfPig/Filters/LzwFilter.cs | 10 +++------- src/UglyToad.PdfPig/Filters/RunLengthFilter.cs | 4 ++-- src/UglyToad.PdfPig/Polyfills/EncodingExtensions.cs | 4 ++-- 18 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/UglyToad.PdfPig.Tests/Filters/Ascii85FilterTests.cs b/src/UglyToad.PdfPig.Tests/Filters/Ascii85FilterTests.cs index a408ebe16..857f01c2c 100644 --- a/src/UglyToad.PdfPig.Tests/Filters/Ascii85FilterTests.cs +++ b/src/UglyToad.PdfPig.Tests/Filters/Ascii85FilterTests.cs @@ -22,7 +22,7 @@ public void DecodesWikipediaExample() var result = filter.Decode(bytes, dictionary, 0); - var text = Encoding.ASCII.GetString(result); + var text = Encoding.ASCII.GetString(result.ToArray()); Assert.Equal("Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, " + "that by a perseverance of delight in the continued and indefatigable generation of knowledge, " + @@ -37,7 +37,7 @@ public void ReplacesZWithEmptyBytes() var result = filter.Decode(bytes, dictionary, 1); - var text = Encoding.ASCII.GetString(result); + var text = Encoding.ASCII.GetString(result.ToArray()); Assert.Equal("Man \0\0\0\0is d", text); } @@ -104,7 +104,7 @@ public void DecodesEncodedPdfContent() var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, 0); - var text = Encoding.ASCII.GetString(result); + var text = Encoding.ASCII.GetString(result.ToArray()); Assert.Equal(PdfContent.Replace("\r\n", "\n"), text); } @@ -124,7 +124,7 @@ public void DecodesEncodedPdfContentMissingEndOfDataSymbol() var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, 0); - var text = Encoding.ASCII.GetString(result); + var text = Encoding.ASCII.GetString(result.ToArray()); Assert.Equal(PdfContent.Replace("\r\n", "\n"), text); } diff --git a/src/UglyToad.PdfPig.Tests/Filters/AsciiHexDecodeFilterTests.cs b/src/UglyToad.PdfPig.Tests/Filters/AsciiHexDecodeFilterTests.cs index 0cf31418b..04f0d4dd6 100644 --- a/src/UglyToad.PdfPig.Tests/Filters/AsciiHexDecodeFilterTests.cs +++ b/src/UglyToad.PdfPig.Tests/Filters/AsciiHexDecodeFilterTests.cs @@ -18,7 +18,7 @@ public void DecodesEncodedTextProperly() var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1); - var decodedText = Encoding.ASCII.GetString(decoded); + var decodedText = Encoding.ASCII.GetString(decoded.ToArray()); Assert.Equal(text, decodedText); } @@ -33,7 +33,7 @@ public void DecodesEncodedTextWithBracesProperly() var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1); - var decodedText = Encoding.ASCII.GetString(decoded); + var decodedText = Encoding.ASCII.GetString(decoded.ToArray()); Assert.Equal(text, decodedText); } @@ -49,7 +49,7 @@ public void DecodesEncodedTextWithWhitespaceProperly() var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1); - var decodedText = Encoding.ASCII.GetString(decoded); + var decodedText = Encoding.ASCII.GetString(decoded.ToArray()); Assert.Equal(text, decodedText); } @@ -63,7 +63,7 @@ public void DecodesEncodedTextLowercaseProperly() var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1); - var decodedText = Encoding.ASCII.GetString(decoded); + var decodedText = Encoding.ASCII.GetString(decoded.ToArray()); Assert.Equal(text, decodedText); } @@ -88,7 +88,7 @@ public void SubstitutesZeroForLastByte() var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1); #pragma warning disable SYSLIB0001 - var decodedText = Encoding.UTF7.GetString(decoded); + var decodedText = Encoding.UTF7.GetString(decoded.ToArray()); #pragma warning restore SYSLIB0001 Assert.Equal("®P", decodedText); @@ -103,7 +103,7 @@ public void DecodesEncodedTextStoppingAtLastBrace() var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1); - var decodedText = Encoding.ASCII.GetString(decoded); + var decodedText = Encoding.ASCII.GetString(decoded.ToArray()); Assert.Equal(text, decodedText); } diff --git a/src/UglyToad.PdfPig.Tests/Filters/CcittFaxDecodeFilterTests.cs b/src/UglyToad.PdfPig.Tests/Filters/CcittFaxDecodeFilterTests.cs index 63d54d816..aa680b26f 100644 --- a/src/UglyToad.PdfPig.Tests/Filters/CcittFaxDecodeFilterTests.cs +++ b/src/UglyToad.PdfPig.Tests/Filters/CcittFaxDecodeFilterTests.cs @@ -32,7 +32,7 @@ public void CanDecodeCCittFaxCompressedImageData() var expectedBytes = ImageHelpers.LoadFileBytes("ccittfax-decoded.bin"); var decodedBytes = filter.Decode(encodedBytes, new DictionaryToken(dictionary), 0); - Assert.Equal(expectedBytes, decodedBytes); + Assert.Equal(expectedBytes, decodedBytes.ToArray()); } } } diff --git a/src/UglyToad.PdfPig.Tests/Filters/FlateFilterTests.cs b/src/UglyToad.PdfPig.Tests/Filters/FlateFilterTests.cs index 2b8bd684d..db2829f0a 100644 --- a/src/UglyToad.PdfPig.Tests/Filters/FlateFilterTests.cs +++ b/src/UglyToad.PdfPig.Tests/Filters/FlateFilterTests.cs @@ -18,7 +18,7 @@ public void EncodeAndDecodePreservesInput() inputStream.Seek(0, SeekOrigin.Begin); var result = filter.Encode(inputStream, parameters, 0); var decoded = filter.Decode(result, parameters, 0); - Assert.Equal(input, decoded); + Assert.Equal(input, decoded.ToArray()); } } } diff --git a/src/UglyToad.PdfPig.Tests/Filters/RunLengthFilterTests.cs b/src/UglyToad.PdfPig.Tests/Filters/RunLengthFilterTests.cs index 0bfa5feff..124147c0e 100644 --- a/src/UglyToad.PdfPig.Tests/Filters/RunLengthFilterTests.cs +++ b/src/UglyToad.PdfPig.Tests/Filters/RunLengthFilterTests.cs @@ -35,7 +35,7 @@ public void CanDecodeRunLengthEncodedData() 10, 19 }; - Assert.Equal(expectedResult, decoded); + Assert.Equal(expectedResult, decoded.ToArray()); } [Fact] @@ -61,7 +61,7 @@ public void StopsAtEndOfDataByte() 128, 50 }; - Assert.Equal(expectedResult, decoded); + Assert.Equal(expectedResult, decoded.ToArray()); } } } diff --git a/src/UglyToad.PdfPig.Tests/Integration/PdfParserTests.cs b/src/UglyToad.PdfPig.Tests/Integration/PdfParserTests.cs index 79cc785d3..894d46bae 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/PdfParserTests.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/PdfParserTests.cs @@ -96,14 +96,14 @@ public void CanDecompressPngEncodedFlateStream() }); var filter = new FlateFilter(); - var filtered = filter.Decode(streamBytes, dictionary, 0); + var filtered = filter.Decode(streamBytes, dictionary, 0).ToArray(); var expected = "1 0 15 0 1 0 216 0 1 2 160 0 1 2 210 0 1 3 84 0 1 4 46 0 1 7 165 0 1 70 229 0 1 72 84 0 1 96 235 0 1 98 18 0 2 0 12 0 2 0 12 1 2 0 12 2 2 0 12 3 2 0 12 4 2 0 12 5 2 0 12 6 2 0 12 7 2 0 12 8" .Split(' ') .Select(byte.Parse).ToArray(); - Assert.Equal(filtered, expected); + Assert.Equal(expected, filtered); } /// diff --git a/src/UglyToad.PdfPig/Content/InlineImage.cs b/src/UglyToad.PdfPig/Content/InlineImage.cs index 6ec315d28..7c8c0f5ec 100644 --- a/src/UglyToad.PdfPig/Content/InlineImage.cs +++ b/src/UglyToad.PdfPig/Content/InlineImage.cs @@ -93,14 +93,14 @@ internal InlineImage(PdfRectangle bounds, int widthInSamples, int heightInSample memoryFactory = supportsFilters ? new Lazy>(() => { - var b = rawMemory.Span; + var b = RawMemory; for (var i = 0; i < filters.Count; i++) { var filter = filters[i]; - b = filter.Decode(b, streamDictionary, i); + b = filter.Decode(b.Span, streamDictionary, i); } - return b.ToArray(); + return b; }) : null; } diff --git a/src/UglyToad.PdfPig/Filters/Ascii85Filter.cs b/src/UglyToad.PdfPig/Filters/Ascii85Filter.cs index 71f55c55d..c0657a63f 100644 --- a/src/UglyToad.PdfPig/Filters/Ascii85Filter.cs +++ b/src/UglyToad.PdfPig/Filters/Ascii85Filter.cs @@ -1,7 +1,6 @@ namespace UglyToad.PdfPig.Filters { using System; - using System.Collections.Generic; using Core; using Tokens; @@ -9,7 +8,7 @@ /// /// ASCII 85 (Base85) is a binary to text encoding using 5 ASCII characters per 4 bytes of data. /// - internal class Ascii85Filter : IFilter + internal sealed class Ascii85Filter : IFilter { private const byte EmptyBlock = (byte)'z'; private const byte Offset = (byte)'!'; @@ -29,7 +28,7 @@ internal class Ascii85Filter : IFilter public bool IsSupported { get; } = true; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { var asciiBuffer = new byte[5]; @@ -98,8 +97,7 @@ public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, WriteData(asciiBuffer, index, writer); } - return writer.WrittenSpan.ToArray(); - + return writer.WrittenMemory; } private static void WriteData(Span ascii, int index, ArrayPoolBufferWriter writer) diff --git a/src/UglyToad.PdfPig/Filters/AsciiHexDecodeFilter.cs b/src/UglyToad.PdfPig/Filters/AsciiHexDecodeFilter.cs index 81d6bca67..d61fdd9b4 100644 --- a/src/UglyToad.PdfPig/Filters/AsciiHexDecodeFilter.cs +++ b/src/UglyToad.PdfPig/Filters/AsciiHexDecodeFilter.cs @@ -29,7 +29,7 @@ internal sealed class AsciiHexDecodeFilter : IFilter public bool IsSupported { get; } = true; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { Span pair = stackalloc byte[2]; var index = 0; @@ -69,7 +69,7 @@ public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, WriteHexToByte(pair, writer); } - return writer.WrittenSpan.ToArray(); + return writer.WrittenMemory; } private static void WriteHexToByte(ReadOnlySpan hexBytes, ArrayPoolBufferWriter writer) diff --git a/src/UglyToad.PdfPig/Filters/CcittFaxDecodeFilter.cs b/src/UglyToad.PdfPig/Filters/CcittFaxDecodeFilter.cs index e8bae30e6..f0520ecd2 100644 --- a/src/UglyToad.PdfPig/Filters/CcittFaxDecodeFilter.cs +++ b/src/UglyToad.PdfPig/Filters/CcittFaxDecodeFilter.cs @@ -16,7 +16,7 @@ internal sealed class CcittFaxDecodeFilter : IFilter public bool IsSupported { get; } = true; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { var decodeParms = DecodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex); diff --git a/src/UglyToad.PdfPig/Filters/DctDecodeFilter.cs b/src/UglyToad.PdfPig/Filters/DctDecodeFilter.cs index 624496446..818604961 100644 --- a/src/UglyToad.PdfPig/Filters/DctDecodeFilter.cs +++ b/src/UglyToad.PdfPig/Filters/DctDecodeFilter.cs @@ -3,13 +3,13 @@ using System; using Tokens; - internal class DctDecodeFilter : IFilter + internal sealed class DctDecodeFilter : IFilter { /// public bool IsSupported { get; } = false; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { throw new NotSupportedException("The DST (Discrete Cosine Transform) Filter indicates data is encoded in JPEG format. " + "This filter is not currently supported but the raw data can be supplied to JPEG supporting libraries."); diff --git a/src/UglyToad.PdfPig/Filters/FlateFilter.cs b/src/UglyToad.PdfPig/Filters/FlateFilter.cs index dc3de8609..e609a348c 100644 --- a/src/UglyToad.PdfPig/Filters/FlateFilter.cs +++ b/src/UglyToad.PdfPig/Filters/FlateFilter.cs @@ -32,7 +32,7 @@ internal sealed class FlateFilter : IFilter public bool IsSupported { get; } = true; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { var parameters = DecodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex); @@ -52,9 +52,7 @@ public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, var bitsPerComponent = parameters.GetIntOrDefault(NameToken.BitsPerComponent, DefaultBitsPerComponent); var columns = parameters.GetIntOrDefault(NameToken.Columns, DefaultColumns); - var result = PngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns); - - return result; + return PngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns); } catch { @@ -64,7 +62,7 @@ public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, return bytes; } - private byte[] Decompress(byte[] input) + private static byte[] Decompress(byte[] input) { using (var memoryStream = new MemoryStream(input)) using (var output = new MemoryStream()) diff --git a/src/UglyToad.PdfPig/Filters/IFilter.cs b/src/UglyToad.PdfPig/Filters/IFilter.cs index 6765ebec0..da5ceaacb 100644 --- a/src/UglyToad.PdfPig/Filters/IFilter.cs +++ b/src/UglyToad.PdfPig/Filters/IFilter.cs @@ -20,6 +20,6 @@ public interface IFilter /// The dictionary of the (or other dictionary types, e.g. inline images) containing these bytes. /// The position of this filter in the pipeline used to encode data. /// The decoded bytes. - byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex); + ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex); } } diff --git a/src/UglyToad.PdfPig/Filters/Jbig2DecodeFilter.cs b/src/UglyToad.PdfPig/Filters/Jbig2DecodeFilter.cs index 05c698a28..7850a74b7 100644 --- a/src/UglyToad.PdfPig/Filters/Jbig2DecodeFilter.cs +++ b/src/UglyToad.PdfPig/Filters/Jbig2DecodeFilter.cs @@ -9,7 +9,7 @@ internal sealed class Jbig2DecodeFilter : IFilter public bool IsSupported { get; } = false; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { throw new NotSupportedException("The JBIG2 Filter for monochrome image data is not currently supported. " + "Try accessing the raw compressed data directly."); diff --git a/src/UglyToad.PdfPig/Filters/JpxDecodeFilter.cs b/src/UglyToad.PdfPig/Filters/JpxDecodeFilter.cs index ef31a83ec..984bf6cd4 100644 --- a/src/UglyToad.PdfPig/Filters/JpxDecodeFilter.cs +++ b/src/UglyToad.PdfPig/Filters/JpxDecodeFilter.cs @@ -3,13 +3,13 @@ using System; using Tokens; - internal class JpxDecodeFilter : IFilter + internal sealed class JpxDecodeFilter : IFilter { /// public bool IsSupported { get; } = false; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { throw new NotSupportedException("The JPX Filter (JPEG2000) for image data is not currently supported. " + "Try accessing the raw compressed data directly."); diff --git a/src/UglyToad.PdfPig/Filters/LzwFilter.cs b/src/UglyToad.PdfPig/Filters/LzwFilter.cs index 119607788..eaaae251b 100644 --- a/src/UglyToad.PdfPig/Filters/LzwFilter.cs +++ b/src/UglyToad.PdfPig/Filters/LzwFilter.cs @@ -29,7 +29,7 @@ internal sealed class LzwFilter : IFilter public bool IsSupported { get; } = true; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { var parameters = DecodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex); @@ -45,14 +45,10 @@ public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, var bitsPerComponent = parameters.GetIntOrDefault(NameToken.BitsPerComponent, DefaultBitsPerComponent); var columns = parameters.GetIntOrDefault(NameToken.Columns, DefaultColumns); - var result = PngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns); - - return result; + return PngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns); } - var data = Decode(input, earlyChange == 1); - - return data; + return Decode(input, earlyChange == 1); } private static byte[] Decode(ReadOnlySpan input, bool isEarlyChange) diff --git a/src/UglyToad.PdfPig/Filters/RunLengthFilter.cs b/src/UglyToad.PdfPig/Filters/RunLengthFilter.cs index 40b49620b..6e8845d3f 100644 --- a/src/UglyToad.PdfPig/Filters/RunLengthFilter.cs +++ b/src/UglyToad.PdfPig/Filters/RunLengthFilter.cs @@ -17,7 +17,7 @@ internal sealed class RunLengthFilter : IFilter public bool IsSupported { get; } = true; /// - public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) + public ReadOnlyMemory Decode(ReadOnlySpan input, DictionaryToken streamDictionary, int filterIndex) { using var output = new ArrayPoolBufferWriter(input.Length); @@ -63,7 +63,7 @@ public byte[] Decode(ReadOnlySpan input, DictionaryToken streamDictionary, } } - return output.WrittenSpan.ToArray(); + return output.WrittenMemory; } } } \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Polyfills/EncodingExtensions.cs b/src/UglyToad.PdfPig/Polyfills/EncodingExtensions.cs index 9c4e7a12d..d159a75fe 100644 --- a/src/UglyToad.PdfPig/Polyfills/EncodingExtensions.cs +++ b/src/UglyToad.PdfPig/Polyfills/EncodingExtensions.cs @@ -1,10 +1,10 @@ -#if NETFRAMEWORK || NETSTANDARD2_0 +#if !NET namespace System.Text; internal static class EncodingExtensions { - public static string GetString(this Encoding encoding, ReadOnlySpan bytes) + internal static string GetString(this Encoding encoding, ReadOnlySpan bytes) { if (bytes.IsEmpty) {