add a bunch more performance improvements

filter provider becomes single instance and no longer has constructor parameters.

tokenizers use list and stringbuilder pools to reduce allocations.

system font finder becomes static to preserve file cache across all documents.
This commit is contained in:
Eliot Jones
2020-04-05 15:34:47 +01:00
parent 7baa18b5dd
commit f1be6634a7
20 changed files with 174 additions and 136 deletions

View File

@@ -2,19 +2,11 @@
{
using System;
using System.Collections.Generic;
using Logging;
using Tokens;
internal class DecodeParameterResolver : IDecodeParameterResolver
internal static class DecodeParameterResolver
{
private readonly ILog log;
public DecodeParameterResolver(ILog log)
{
this.log = log;
}
public DictionaryToken GetFilterParameters(DictionaryToken streamDictionary, int index)
public static DictionaryToken GetFilterParameters(DictionaryToken streamDictionary, int index)
{
if (streamDictionary == null)
{
@@ -48,10 +40,6 @@
}
break;
default:
if (parameters != null)
{
log?.Error("Expected the decode parameters for the stream to be either an array or dictionary");
}
break;
}

View File

@@ -5,7 +5,6 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
using Logging;
using Tokens;
using Util;
@@ -30,17 +29,6 @@
private const byte Deflate32KbWindow = 120;
private const byte ChecksumBits = 1;
private readonly IDecodeParameterResolver decodeParameterResolver;
private readonly IPngPredictor pngPredictor;
private readonly ILog log;
public FlateFilter(IDecodeParameterResolver decodeParameterResolver, IPngPredictor pngPredictor, ILog log)
{
this.decodeParameterResolver = decodeParameterResolver;
this.pngPredictor = pngPredictor;
this.log = log;
}
/// <inheritdoc />
public bool IsSupported { get; } = true;
@@ -52,7 +40,7 @@
throw new ArgumentNullException(nameof(input));
}
var parameters = decodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex);
var parameters = DecodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex);
var predictor = parameters.GetIntOrDefault(NameToken.Predictor, -1);
@@ -70,13 +58,13 @@
var bitsPerComponent = parameters.GetIntOrDefault(NameToken.BitsPerComponent, DefaultBitsPerComponent);
var columns = parameters.GetIntOrDefault(NameToken.Columns, DefaultColumns);
var result = pngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns);
var result = PngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns);
return result;
}
catch (Exception ex)
catch
{
log.Error("Could not decode a flate stream due to an error.", ex);
// ignored.
}
return bytes;
@@ -84,27 +72,19 @@
private byte[] Decompress(byte[] input)
{
try
using (var memoryStream = new MemoryStream(input))
using (var output = new MemoryStream())
{
using (var memoryStream = new MemoryStream(input))
using (var output = new MemoryStream())
{
// The first 2 bytes are the header which DeflateStream does not support.
memoryStream.ReadByte();
memoryStream.ReadByte();
// The first 2 bytes are the header which DeflateStream does not support.
memoryStream.ReadByte();
memoryStream.ReadByte();
using (var deflate = new DeflateStream(memoryStream, CompressionMode.Decompress))
{
deflate.CopyTo(output);
return output.ToArray();
}
using (var deflate = new DeflateStream(memoryStream, CompressionMode.Decompress))
{
deflate.CopyTo(output);
return output.ToArray();
}
}
catch (Exception ex)
{
log?.Error("Could not decode the input using the deflate stream. Input was: " + input, ex);
throw;
}
}
public byte[] Encode(Stream input, DictionaryToken streamDictionary, int index)

View File

@@ -1,9 +0,0 @@
namespace UglyToad.PdfPig.Filters
{
using Tokens;
internal interface IDecodeParameterResolver
{
DictionaryToken GetFilterParameters(DictionaryToken streamDictionary, int index);
}
}

View File

@@ -1,7 +0,0 @@
namespace UglyToad.PdfPig.Filters
{
internal interface IPngPredictor
{
byte[] Decode(byte[] input, int predictor, int colors, int bitsPerComponent, int columns);
}
}

View File

@@ -22,23 +22,14 @@
private const int NineBitBoundary = 511;
private const int TenBitBoundary = 1023;
private const int ElevenBitBoundary = 2047;
private readonly IDecodeParameterResolver decodeParameterResolver;
private readonly IPngPredictor pngPredictor;
public LzwFilter(IDecodeParameterResolver decodeParameterResolver, IPngPredictor pngPredictor)
{
this.decodeParameterResolver = decodeParameterResolver ?? throw new ArgumentNullException(nameof(decodeParameterResolver));
this.pngPredictor = pngPredictor ?? throw new ArgumentNullException(nameof(pngPredictor));
}
/// <inheritdoc />
public bool IsSupported { get; } = true;
/// <inheritdoc />
public byte[] Decode(IReadOnlyList<byte> input, DictionaryToken streamDictionary, int filterIndex)
{
var parameters = decodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex);
var parameters = DecodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex);
var predictor = parameters.GetIntOrDefault(NameToken.Predictor, -1);
@@ -52,7 +43,7 @@
var bitsPerComponent = parameters.GetIntOrDefault(NameToken.BitsPerComponent, DefaultBitsPerComponent);
var columns = parameters.GetIntOrDefault(NameToken.Columns, DefaultColumns);
var result = pngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns);
var result = PngPredictor.Decode(decompressed, predictor, colors, bitsPerComponent, columns);
return result;
}
@@ -64,7 +55,8 @@
private static byte[] Decode(IReadOnlyList<byte> input, bool isEarlyChange)
{
var result = new List<byte>();
// A guess.
var result = new List<byte>((int)(input.Count * 1.5));
var table = GetDefaultTable();

View File

@@ -4,25 +4,25 @@
using System.Collections.Generic;
using System.Linq;
using Core;
using Exceptions;
using Logging;
using Tokens;
internal class MemoryFilterProvider : IFilterProvider
{
private readonly IReadOnlyDictionary<string, IFilter> filterInstances;
public MemoryFilterProvider(IDecodeParameterResolver decodeParameterResolver, IPngPredictor pngPredictor, ILog log)
public static readonly IFilterProvider Instance = new MemoryFilterProvider();
private MemoryFilterProvider()
{
var ascii85 = new Ascii85Filter();
var asciiHex = new AsciiHexDecodeFilter();
var ccitt = new CcittFaxDecodeFilter();
var dct = new DctDecodeFilter();
var flate = new FlateFilter(decodeParameterResolver, pngPredictor, log);
var flate = new FlateFilter();
var jbig2 = new Jbig2DecodeFilter();
var jpx = new JpxDecodeFilter();
var runLength = new RunLengthFilter();
var lzw = new LzwFilter(decodeParameterResolver, pngPredictor);
var lzw = new LzwFilter();
filterInstances = new Dictionary<string, IFilter>
{

View File

@@ -5,9 +5,9 @@
using System.IO;
using IO;
internal class PngPredictor : IPngPredictor
internal static class PngPredictor
{
public byte[] Decode(byte[] inputBytes, int predictor, int colors, int bitsPerComponent, int columns)
public static byte[] Decode(byte[] inputBytes, int predictor, int colors, int bitsPerComponent, int columns)
{
if (inputBytes == null)
{

View File

@@ -39,10 +39,10 @@
throw new ArgumentNullException(nameof(array));
}
foreach (var token in array)
for (var i = 0; i < array.Count; i++)
{
if (!(token is NumericToken) && !(token is HexToken)
&& !(token is StringToken))
var token = array[i];
if (!(token is StringToken) && !(token is NumericToken) && !(token is HexToken))
{
throw new ArgumentException($"Found invalid token for showing texts with position: {token}");
}

View File

@@ -1,3 +1,5 @@
using UglyToad.PdfPig.Tokenization;
namespace UglyToad.PdfPig.Graphics
{
using System;
@@ -21,6 +23,8 @@ namespace UglyToad.PdfPig.Graphics
internal class ReflectionGraphicsStateOperationFactory : IGraphicsStateOperationFactory
{
private static readonly ListPool<decimal> DecimalListPool = new ListPool<decimal>(10);
private readonly IReadOnlyDictionary<string, Type> operations;
public ReflectionGraphicsStateOperationFactory()
@@ -51,7 +55,7 @@ namespace UglyToad.PdfPig.Graphics
private static decimal[] TokensToDecimalArray(IReadOnlyList<IToken> tokens, bool exceptLast = false)
{
var result = new List<decimal>();
var result = DecimalListPool.Borrow();
for (var i = 0; i < tokens.Count - (exceptLast ? 1 : 0); i++)
{
@@ -65,7 +69,9 @@ namespace UglyToad.PdfPig.Graphics
if (!(innerOperand is NumericToken innerNumeric))
{
return result.ToArray();
var val = result.ToArray();
DecimalListPool.Return(result);
return val.ToArray();
}
result.Add(innerNumeric.Data);
@@ -74,13 +80,17 @@ namespace UglyToad.PdfPig.Graphics
if (!(operand is NumericToken numeric))
{
return result.ToArray();
var val = result.ToArray();
DecimalListPool.Return(result);
return val.ToArray();
}
result.Add(numeric.Data);
}
return result.ToArray();
var returnValue = result.ToArray();
DecimalListPool.Return(result);
return returnValue;
}
private static int OperandToInt(IToken token)

View File

@@ -80,7 +80,7 @@
private static PdfDocument OpenDocument(IInputBytes inputBytes, ISeekableTokenScanner scanner, ILog log, bool isLenientParsing, IReadOnlyList<string> passwords)
{
var filterProvider = new MemoryFilterProvider(new DecodeParameterResolver(log), new PngPredictor(), log);
var filterProvider = MemoryFilterProvider.Instance;
CrossReferenceTable crossReferenceTable = null;
@@ -127,7 +127,7 @@
var fontFactory = new FontFactory(log, new Type0FontHandler(cidFontFactory,
filterProvider, pdfScanner),
new TrueTypeFontHandler(log, pdfScanner, filterProvider, encodingReader, new SystemFontFinder(),
new TrueTypeFontHandler(log, pdfScanner, filterProvider, encodingReader, SystemFontFinder.Instance,
type1Handler),
type1Handler,
new Type3FontHandler(pdfScanner, filterProvider, encodingReader));

View File

@@ -4,7 +4,6 @@
using System.IO;
using System.Linq;
using Filters;
using Logging;
using Tokens;
internal static class DataCompresser
@@ -15,7 +14,7 @@
using (var memoryStream = new MemoryStream(bytes))
{
var parameters = new DictionaryToken(new Dictionary<NameToken, IToken>());
var flater = new FlateFilter(new DecodeParameterResolver(new NoOpLog()), new PngPredictor(), new NoOpLog());
var flater = new FlateFilter();
var result = flater.Encode(memoryStream, parameters, 0);
return result;
}

View File

@@ -24,8 +24,7 @@
{
private static readonly ILog Log = new NoOpLog();
private static readonly IFilterProvider FilterProvider = new MemoryFilterProvider(new DecodeParameterResolver(Log),
new PngPredictor(), Log);
private static readonly IFilterProvider FilterProvider = MemoryFilterProvider.Instance;
/// <summary>
/// Merge two PDF documents together with the pages from <paramref name="file1"/> followed by <paramref name="file2"/>.