Improve Code Quality 3 (#805)

* Throw when trying to inverse a matrix with a determinant of 0

* Optimize Hex.GetString on .NET

* Updates tests for Matrix3x3.Inverse() change

* Eliminate allocation in InternalStringExtensions

* Use vectorized Span.Fill method

* Eliminate various string allocations when parsing numbers

* Remove unused using statements

* Fix Matrix3x3 Equals nullability
This commit is contained in:
Jason Nelson 2024-03-17 14:13:32 -07:00 committed by GitHub
parent a412a239be
commit 69e2b7bb08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 55 additions and 64 deletions

View File

@ -15,7 +15,7 @@ namespace UglyToad.PdfPig.Tests.Util
[InlineData("pineapple", "apple", 4, true)] [InlineData("pineapple", "apple", 4, true)]
public void StartsWithOffset(string input, string start, int offset, bool expected) public void StartsWithOffset(string input, string start, int offset, bool expected)
{ {
var result = input.StartsWithOffset(start, offset); var result = input.StartsWithOffset(start.AsSpan(), offset);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -23,9 +23,9 @@ namespace UglyToad.PdfPig.Tests.Util
[Fact] [Fact]
public void StartsWithOffset_NegativeOffset_Throws() public void StartsWithOffset_NegativeOffset_Throws()
{ {
Action action = () => "any".StartsWithOffset("x", -1); Action action = () => "any".StartsWithOffset("x".AsSpan(), -1);
Assert.Throws<ArgumentOutOfRangeException>(action); Assert.Throws<ArgumentOutOfRangeException>(action);
} }
} }
} }

View File

@ -106,15 +106,14 @@
} }
[Fact] [Fact]
public void InverseReturnsNullIfNotPossible() public void InverseThrowsNotPossible()
{ {
var matrix = new Matrix3x3( var matrix = new Matrix3x3(
1, 2, 3, 1, 2, 3,
4, 5, 6, 4, 5, 6,
7, 8, 9); 7, 8, 9);
var inverse = matrix.Inverse(); Assert.Throws<InvalidOperationException>(() => matrix.Inverse());
Assert.Null(inverse);
} }
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.IO; using System.IO;
using IO; using IO;
using Util;
/// <summary> /// <summary>
/// CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression. /// CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression.
@ -426,7 +425,7 @@
{ {
if (decodedLength < 0) if (decodedLength < 0)
{ {
ArrayHelper.Fill(b, off, off + len, (byte)0x0); b.AsSpan(off, len).Fill(0x0);
return len; return len;
} }
@ -436,7 +435,7 @@
if (decodedLength < 0) if (decodedLength < 0)
{ {
ArrayHelper.Fill(b, off, off + len, (byte)0x0); b.AsSpan(off, len).Fill(0x0);
return len; return len;
} }
} }

View File

@ -18,7 +18,7 @@
Method method = Method.Bradford) Method method = Method.Bradford)
{ {
var coneReponseDomain = GetConeResponseDomain(method)!; var coneReponseDomain = GetConeResponseDomain(method)!;
var inverseConeResponseDomain = coneReponseDomain.Inverse()!; var inverseConeResponseDomain = coneReponseDomain.Inverse();
var (ρS, γS, βS) = coneReponseDomain.Multiply(sourceReferenceWhite); var (ρS, γS, βS) = coneReponseDomain.Multiply(sourceReferenceWhite);
var (ρD, γD, βD) = coneReponseDomain.Multiply(destinationReferenceWhite); var (ρD, γD, βD) = coneReponseDomain.Multiply(destinationReferenceWhite);

View File

@ -8,6 +8,7 @@
using Logging; using Logging;
using Tokenization.Scanner; using Tokenization.Scanner;
using Tokens; using Tokens;
using UglyToad.PdfPig.Util;
/// <summary> /// <summary>
/// Used to retrieve the version header from the PDF file. /// Used to retrieve the version header from the PDF file.
@ -70,7 +71,7 @@
const int toDoubleStartLength = 4; const int toDoubleStartLength = 4;
if (!double.TryParse(comment.Data.Substring(toDoubleStartLength), if (!double.TryParse(comment.Data.AsSpanOrSubstring(toDoubleStartLength),
NumberStyles.Number, NumberStyles.Number,
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
out var version)) out var version))
@ -118,7 +119,7 @@
if (actualIndex >= 0 && content.Length - actualIndex >= versionLength) if (actualIndex >= 0 && content.Length - actualIndex >= versionLength)
{ {
var numberPart = content.Substring(actualIndex + 5, 3); var numberPart = content.AsSpanOrSubstring(actualIndex + 5, 3);
if (double.TryParse( if (double.TryParse(
numberPart, numberPart,
NumberStyles.Number, NumberStyles.Number,

View File

@ -1,30 +0,0 @@
namespace UglyToad.PdfPig.Util
{
using System;
internal static class ArrayHelper
{
public static void Fill<T>(T[] array, int start, int end, T value)
{
if (array is null)
{
throw new ArgumentNullException(nameof(array));
}
if (start < 0 || start >= end)
{
throw new ArgumentOutOfRangeException(nameof(start));
}
if (end >= array.Length)
{
throw new ArgumentOutOfRangeException(nameof(end));
}
for (int i = start; i < end; i++)
{
array[i] = value;
}
}
}
}

View File

@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Util namespace UglyToad.PdfPig.Util
{ {
using System; using System;
using System.Globalization;
/// <summary> /// <summary>
/// Helper class for dates. /// Helper class for dates.
@ -53,7 +54,7 @@
return false; return false;
} }
if (!int.TryParse(s.Substring(location, 4), out var year)) if (!int.TryParse(s.AsSpanOrSubstring(location, 4), NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
{ {
return false; return false;
} }
@ -72,7 +73,7 @@
return true; return true;
} }
if (!int.TryParse(s.Substring(location, 2), out var month) if (!int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var month)
|| !IsWithinRange(month, 1, 12)) || !IsWithinRange(month, 1, 12))
{ {
return false; return false;
@ -92,7 +93,7 @@
return true; return true;
} }
if (!int.TryParse(s.Substring(location, 2), out var day) if (!int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var day)
|| !IsWithinRange(day, 1, 31)) || !IsWithinRange(day, 1, 31))
{ {
return false; return false;
@ -112,7 +113,7 @@
return true; return true;
} }
if (!int.TryParse(s.Substring(location, 2), out var hour) if (!int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var hour)
|| !IsWithinRange(hour, 0, 23)) || !IsWithinRange(hour, 0, 23))
{ {
return false; return false;
@ -132,7 +133,7 @@
return true; return true;
} }
if (!int.TryParse(s.Substring(location, 2), out var minute) if (!int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var minute)
|| !IsWithinRange(minute, 0, 59)) || !IsWithinRange(minute, 0, 59))
{ {
return false; return false;
@ -152,7 +153,7 @@
return true; return true;
} }
if (!int.TryParse(s.Substring(location, 2), out var second) if (!int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var second)
|| !IsWithinRange(second, 0, 59)) || !IsWithinRange(second, 0, 59))
{ {
return false; return false;
@ -189,7 +190,7 @@
return true; return true;
} }
if (!HasRemainingCharacters(location, 3) || !int.TryParse(s.Substring(location, 2), out var hoursOffset) if (!HasRemainingCharacters(location, 3) || !int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var hoursOffset)
|| s[location + 2] != '\'' || s[location + 2] != '\''
|| !IsWithinRange(hoursOffset, 0, 23)) || !IsWithinRange(hoursOffset, 0, 23))
{ {
@ -205,7 +206,7 @@
return true; return true;
} }
if (!HasRemainingCharacters(location, 3) || !int.TryParse(s.Substring(location, 2), out var minutesOffset) if (!HasRemainingCharacters(location, 3) || !int.TryParse(s.AsSpanOrSubstring(location, 2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var minutesOffset)
|| s[location + 2] != '\'' || s[location + 2] != '\''
|| !IsWithinRange(minutesOffset, 0, 59)) || !IsWithinRange(minutesOffset, 0, 59))
{ {

View File

@ -23,6 +23,9 @@
/// </summary> /// </summary>
public static string GetString(ReadOnlySpan<byte> bytes) public static string GetString(ReadOnlySpan<byte> bytes)
{ {
#if NET6_0_OR_GREATER
return Convert.ToHexString(bytes);
#else
var stringBuilder = new StringBuilder(bytes.Length * 2); var stringBuilder = new StringBuilder(bytes.Length * 2);
foreach (var b in bytes) foreach (var b in bytes)
@ -31,6 +34,7 @@
} }
return stringBuilder.ToString(); return stringBuilder.ToString();
#endif
} }
private static int GetHighNibble(byte b) private static int GetHighNibble(byte b)

View File

@ -4,7 +4,7 @@
internal static class InternalStringExtensions internal static class InternalStringExtensions
{ {
public static bool StartsWithOffset(this string value, string start, int offset) public static bool StartsWithOffset(this string value, ReadOnlySpan<char> start, int offset)
{ {
if (offset < 0) if (offset < 0)
{ {
@ -13,7 +13,7 @@
if (value is null) if (value is null)
{ {
if (start is null && offset == 0) if (start.Length is 0 && offset == 0)
{ {
return true; return true;
} }
@ -26,7 +26,29 @@
return false; return false;
} }
return value.Substring(offset).StartsWith(start); return value.AsSpan(offset).StartsWith(start, StringComparison.Ordinal);
} }
#if NET
public static ReadOnlySpan<char> AsSpanOrSubstring(this string text, int start)
{
return text.AsSpan(start);
}
public static ReadOnlySpan<char> AsSpanOrSubstring(this string text, int start, int length)
{
return text.AsSpan(start, length);
}
#else
public static string AsSpanOrSubstring(this string text, int start)
{
return text.Substring(start);
}
public static string AsSpanOrSubstring(this string text, int start, int length)
{
return text.Substring(start, length);
}
#endif
} }
} }

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace UglyToad.PdfPig.Util namespace UglyToad.PdfPig.Util
{ {
internal class Matrix3x3 : IEnumerable<double>, IEquatable<Matrix3x3> internal sealed class Matrix3x3 : IEnumerable<double>, IEquatable<Matrix3x3>
{ {
/// <summary> /// <summary>
/// The identity matrix. The result of multiplying a matrix with /// The identity matrix. The result of multiplying a matrix with
@ -69,14 +69,14 @@ namespace UglyToad.PdfPig.Util
/// ///
/// If an inverse matrix does not exist, null is returned. /// If an inverse matrix does not exist, null is returned.
/// </summary> /// </summary>
public Matrix3x3? Inverse() public Matrix3x3 Inverse()
{ {
var determinant = GetDeterminant(); var determinant = GetDeterminant();
// No inverse matrix exists when determinant is zero // No inverse matrix exists when determinant is zero
if (determinant == 0) if (determinant == 0)
{ {
return null; throw new InvalidOperationException("May not inverse a matrix with a determinant of 0.");
} }
var transposed = Transpose(); var transposed = Transpose();
@ -162,7 +162,7 @@ namespace UglyToad.PdfPig.Util
public bool Equals(Matrix3x3? other) public bool Equals(Matrix3x3? other)
{ {
if (other is null) if (other is null)
{ {
return false; return false;
} }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using UglyToad.PdfPig.Tokens; using UglyToad.PdfPig.Tokens;
namespace UglyToad.PdfPig.Writer namespace UglyToad.PdfPig.Writer

View File

@ -1,9 +1,5 @@
namespace UglyToad.PdfPig.Writer namespace UglyToad.PdfPig.Writer
{ {
using System;
using System.Collections.Generic;
using System.Text;
/// <summary> /// <summary>
/// Type of pdf writer to use. /// Type of pdf writer to use.
/// </summary> /// </summary>
@ -18,4 +14,4 @@
/// </summary> /// </summary>
ObjectInMemoryDedup ObjectInMemoryDedup
} }
} }