From e01bf5e84976a4f2d49e817929e6a3d9d98c1c3e Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Mon, 1 Apr 2024 21:38:42 -0700 Subject: [PATCH] Spanify IInputBytes --- src/UglyToad.PdfPig.Core/IInputBytes.cs | 3 +- src/UglyToad.PdfPig.Core/MemoryInputBytes.cs | 24 ++------ .../Polyfills/StreamExtensions.cs | 28 +++++++-- src/UglyToad.PdfPig.Core/StreamInputBytes.cs | 25 ++------ .../TrueType/TrueTypeDataBytes.cs | 59 +++++++++++-------- .../FileStructure/CrossReferenceParser.cs | 5 +- .../Parser/FileStructure/FileHeaderParser.cs | 4 +- 7 files changed, 71 insertions(+), 77 deletions(-) diff --git a/src/UglyToad.PdfPig.Core/IInputBytes.cs b/src/UglyToad.PdfPig.Core/IInputBytes.cs index efdf6d9c7..834f5f39b 100644 --- a/src/UglyToad.PdfPig.Core/IInputBytes.cs +++ b/src/UglyToad.PdfPig.Core/IInputBytes.cs @@ -47,8 +47,7 @@ public interface IInputBytes : IDisposable /// Fill the buffer with bytes starting from the current position. /// /// A buffer with a length corresponding to the number of bytes to read. - /// Optional override for the number of bytes to read. /// The number of bytes successfully read. - int Read(byte[] buffer, int? length = null); + int Read(Span buffer); } } \ No newline at end of file diff --git a/src/UglyToad.PdfPig.Core/MemoryInputBytes.cs b/src/UglyToad.PdfPig.Core/MemoryInputBytes.cs index ff91ce8d6..e4bac1ee7 100644 --- a/src/UglyToad.PdfPig.Core/MemoryInputBytes.cs +++ b/src/UglyToad.PdfPig.Core/MemoryInputBytes.cs @@ -7,7 +7,7 @@ /// /// Input bytes from a byte array. /// - public class MemoryInputBytes : IInputBytes + public sealed class MemoryInputBytes : IInputBytes { private readonly int upperBound; private readonly ReadOnlyMemory memory; @@ -73,31 +73,15 @@ public void Seek(long position) } /// - public int Read(byte[] buffer, int? length = null) + public int Read(Span buffer) { - var bytesToRead = buffer.Length; - if (length.HasValue) - { - if (length.Value < 0) - { - throw new ArgumentOutOfRangeException($"Cannot use a negative length: {length.Value}."); - } - - if (length.Value > bytesToRead) - { - throw new ArgumentOutOfRangeException($"Cannot read more bytes {length.Value} than there is space in the buffer {buffer.Length}."); - } - - bytesToRead = length.Value; - } - - if (bytesToRead == 0) + if (buffer.IsEmpty) { return 0; } var viableLength = (memory.Length - currentOffset - 1); - var readLength = viableLength < bytesToRead ? viableLength : bytesToRead; + var readLength = viableLength < buffer.Length ? viableLength : buffer.Length; var startFrom = currentOffset + 1; memory.Span.Slice(startFrom, readLength).CopyTo(buffer); diff --git a/src/UglyToad.PdfPig.Core/Polyfills/StreamExtensions.cs b/src/UglyToad.PdfPig.Core/Polyfills/StreamExtensions.cs index 2792cfed6..cb2daf76f 100644 --- a/src/UglyToad.PdfPig.Core/Polyfills/StreamExtensions.cs +++ b/src/UglyToad.PdfPig.Core/Polyfills/StreamExtensions.cs @@ -6,19 +6,37 @@ namespace System.IO; internal static class StreamExtensions { - public static void Write(this Stream stream, ReadOnlySpan data) + public static void Write(this Stream stream, ReadOnlySpan buffer) { - var buffer = ArrayPool.Shared.Rent(data.Length); + var tempBuffer = ArrayPool.Shared.Rent(buffer.Length); - data.CopyTo(buffer); + buffer.CopyTo(tempBuffer); try { - stream.Write(buffer, 0, data.Length); + stream.Write(tempBuffer, 0, buffer.Length); } finally { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(tempBuffer); + } + } + + public static int Read(this Stream stream, Span buffer) + { + var tempBuffer = ArrayPool.Shared.Rent(buffer.Length); + + try + { + int read = stream.Read(tempBuffer, 0, buffer.Length); + + tempBuffer.AsSpan(0, read).CopyTo(buffer); + + return read; + } + finally + { + ArrayPool.Shared.Return(tempBuffer); } } } diff --git a/src/UglyToad.PdfPig.Core/StreamInputBytes.cs b/src/UglyToad.PdfPig.Core/StreamInputBytes.cs index 562432f25..ab1ef0574 100644 --- a/src/UglyToad.PdfPig.Core/StreamInputBytes.cs +++ b/src/UglyToad.PdfPig.Core/StreamInputBytes.cs @@ -7,7 +7,7 @@ /// /// Input bytes from a stream. /// - public class StreamInputBytes : IInputBytes + public sealed class StreamInputBytes : IInputBytes { private readonly Stream stream; private readonly bool shouldDispose; @@ -106,30 +106,15 @@ public void Seek(long position) } /// - public int Read(byte[] buffer, int? length = null) + public int Read(Span buffer) { - var bytesToRead = buffer.Length; - if (length.HasValue) - { - if (length.Value < 0) - { - throw new ArgumentOutOfRangeException($"Cannot use a negative length: {length.Value}."); - } - - if (length.Value > bytesToRead) - { - throw new ArgumentOutOfRangeException($"Cannot read more bytes {length.Value} than there is space in the buffer {buffer.Length}."); - } - - bytesToRead = length.Value; - } - - if (bytesToRead == 0) + if (buffer.IsEmpty) { return 0; } - var read = stream.Read(buffer, 0, bytesToRead); + int read = stream.Read(buffer); + if (read > 0) { CurrentByte = buffer[read - 1]; diff --git a/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeDataBytes.cs b/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeDataBytes.cs index f01b91c77..3dc4d55e7 100644 --- a/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeDataBytes.cs +++ b/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeDataBytes.cs @@ -1,6 +1,7 @@ namespace UglyToad.PdfPig.Fonts.TrueType { using System; + using System.Buffers.Binary; using System.Text; using Core; @@ -9,7 +10,6 @@ /// public class TrueTypeDataBytes { - private readonly byte[] internalBuffer = new byte[16]; private readonly IInputBytes inputBytes; /// @@ -50,9 +50,11 @@ public float Read32Fixed() /// public short ReadSignedShort() { - ReadBuffered(internalBuffer, 2); + Span buffer = stackalloc byte[2]; - return unchecked((short)((internalBuffer[0] << 8) + (internalBuffer[1] << 0))); + ReadBuffered(buffer); + + return unchecked((short)((buffer[0] << 8) + (buffer[1] << 0))); } /// @@ -60,9 +62,11 @@ public short ReadSignedShort() /// public ushort ReadUnsignedShort() { - ReadBuffered(internalBuffer, 2); + Span buffer = stackalloc byte[2]; + + ReadBuffered(buffer); - return (ushort)((internalBuffer[0] << 8) + (internalBuffer[1] << 0)); + return (ushort)((buffer[0] << 8) + (buffer[1] << 0)); } /// @@ -70,9 +74,11 @@ public ushort ReadUnsignedShort() /// public byte ReadByte() { - ReadBuffered(internalBuffer, 1); + Span buffer = stackalloc byte[1]; + + ReadBuffered(buffer); - return internalBuffer[0]; + return buffer[0]; } /// @@ -99,8 +105,11 @@ public bool TryReadString(int bytesToRead, Encoding encoding, out string result) return false; } - byte[] data = new byte[bytesToRead]; - if (ReadBuffered(data, bytesToRead)) + Span data = bytesToRead <= 64 + ? stackalloc byte[bytesToRead] + : new byte[bytesToRead]; + + if (ReadBuffered(data)) { result = encoding.GetString(data); return true; @@ -114,9 +123,11 @@ public bool TryReadString(int bytesToRead, Encoding encoding, out string result) /// public uint ReadUnsignedInt() { - ReadBuffered(internalBuffer, 4); + Span buffer = stackalloc byte[4]; - return (uint)(((long)internalBuffer[0] << 24) + ((long)internalBuffer[1] << 16) + (internalBuffer[2] << 8) + (internalBuffer[3] << 0)); + ReadBuffered(buffer); + + return BinaryPrimitives.ReadUInt32BigEndian(buffer); } /// @@ -124,9 +135,11 @@ public uint ReadUnsignedInt() /// public int ReadSignedInt() { - ReadBuffered(internalBuffer, 4); + Span buffer = stackalloc byte[4]; + + ReadBuffered(buffer); - return (internalBuffer[0] << 24) + (internalBuffer[1] << 16) + (internalBuffer[2] << 8) + (internalBuffer[3] << 0); + return BinaryPrimitives.ReadInt32BigEndian(buffer); } /// @@ -175,9 +188,11 @@ public void Seek(long position) /// public int ReadSignedByte() { - ReadBuffered(internalBuffer, 1); + Span buffer = stackalloc byte[1]; - var signedByte = internalBuffer[0]; + ReadBuffered(buffer); + + var signedByte = buffer[0]; return signedByte < 127 ? signedByte : signedByte - 256; } @@ -204,7 +219,7 @@ public byte[] ReadByteArray(int length) { var result = new byte[length]; - ReadBuffered(result, length); + ReadBuffered(result); return result; } @@ -244,15 +259,9 @@ public override string ToString() return $"@: {Position} of {inputBytes.Length} bytes."; } - private bool ReadBuffered(byte[] buffer, int length) + private bool ReadBuffered(Span buffer) { - var read = inputBytes.Read(buffer, length); - if (read < length) - { - return false; - } - - return true; + return inputBytes.Read(buffer) == buffer.Length; } } -} +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Parser/FileStructure/CrossReferenceParser.cs b/src/UglyToad.PdfPig/Parser/FileStructure/CrossReferenceParser.cs index 2bb7ed349..8541b53a4 100644 --- a/src/UglyToad.PdfPig/Parser/FileStructure/CrossReferenceParser.cs +++ b/src/UglyToad.PdfPig/Parser/FileStructure/CrossReferenceParser.cs @@ -312,7 +312,7 @@ private bool TryBruteForceXrefTableLocate(IInputBytes bytes, long expectedOffset bytes.Seek(lastOffset); - var buffer = new byte[5]; + Span buffer = stackalloc byte[5]; while (bytes.Read(buffer) == buffer.Length) { @@ -347,9 +347,8 @@ private bool TryBruteForceXrefTableLocate(IInputBytes bytes, long expectedOffset bytes.Seek(lastOffset); } - bytes.Read(buffer); - + bytes.Read(buffer); return false; } diff --git a/src/UglyToad.PdfPig/Parser/FileStructure/FileHeaderParser.cs b/src/UglyToad.PdfPig/Parser/FileStructure/FileHeaderParser.cs index 3f8f9ab52..6b2ccb75d 100644 --- a/src/UglyToad.PdfPig/Parser/FileStructure/FileHeaderParser.cs +++ b/src/UglyToad.PdfPig/Parser/FileStructure/FileHeaderParser.cs @@ -103,13 +103,13 @@ private static bool TryBruteForceVersionLocation(long startPosition, IInputBytes // Slide a window of bufferLength bytes across the file allowing for the fact the version could get split by // the window (so always ensure an overlap of versionLength bytes between the end of the previous and start of the next buffer). - var buffer = new byte[bufferLength]; + Span buffer = stackalloc byte[bufferLength]; var currentOffset = startPosition; int readLength; do { - readLength = inputBytes.Read(buffer, bufferLength); + readLength = inputBytes.Read(buffer); var content = OtherEncodings.BytesAsLatin1String(buffer);