Skip to content

Commit

Permalink
Merge pull request #529 from MihaZupan/cache-parsers
Browse files Browse the repository at this point in the history
Cache the MarkdownParser & Processors
  • Loading branch information
xoofx committed Mar 15, 2021
2 parents e9ea103 + 2f95884 commit 7ad7b55
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 193 deletions.
6 changes: 3 additions & 3 deletions src/Markdig/Markdown.cs
Expand Up @@ -21,14 +21,14 @@ public static partial class Markdown
{
public static readonly string Version = ((AssemblyFileVersionAttribute) typeof(Markdown).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false)[0]).Version;

private static readonly MarkdownPipeline _defaultPipeline = new MarkdownPipelineBuilder().Build();
internal static readonly MarkdownPipeline DefaultPipeline = new MarkdownPipelineBuilder().Build();
private static readonly MarkdownPipeline _defaultTrackTriviaPipeline = new MarkdownPipelineBuilder().EnableTrackTrivia().Build();

private static MarkdownPipeline GetPipeline(MarkdownPipeline? pipeline, string markdown)
{
if (pipeline is null)
{
return _defaultPipeline;
return DefaultPipeline;
}

var selfPipeline = pipeline.Extensions.Find<SelfPipelineExtension>();
Expand Down Expand Up @@ -111,7 +111,7 @@ public static string ToHtml(this MarkdownDocument document, MarkdownPipeline? pi
{
if (document is null) ThrowHelper.ArgumentNullException(nameof(document));

pipeline ??= _defaultPipeline;
pipeline ??= DefaultPipeline;

using var rentedRenderer = pipeline.RentHtmlRenderer();
HtmlRenderer renderer = rentedRenderer.Instance;
Expand Down
137 changes: 70 additions & 67 deletions src/Markdig/Parsers/BlockProcessor.cs
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Markdig.Helpers;
using Markdig.Syntax;
Expand All @@ -16,24 +17,8 @@ namespace Markdig.Parsers
/// </summary>
public class BlockProcessor
{
private readonly BlockProcessor? root;
private int currentStackIndex;
private readonly BlockParserStateCache parserStateCache;
private int originalLineStart = 0;

private BlockProcessor(BlockProcessor root, bool trackTrivia = false)
{
// These properties are not changing between a parent and a children BlockProcessor
this.root = root;
this.parserStateCache = root.parserStateCache;
TrackTrivia = trackTrivia;
Document = root.Document;
Parsers = root.Parsers;

// These properties are local to a state
OpenedBlocks = new List<Block>();
NewBlocks = new Stack<Block>();
}
private int originalLineStart;

/// <summary>
/// Initializes a new instance of the <see cref="BlockProcessor" /> class.
Expand All @@ -46,36 +31,30 @@ private BlockProcessor(BlockProcessor root, bool trackTrivia = false)
/// </exception>
public BlockProcessor(MarkdownDocument document, BlockParserList parsers, MarkdownParserContext? context, bool trackTrivia = false)
{
if (document is null) ThrowHelper.ArgumentNullException(nameof(document));
if (parsers is null) ThrowHelper.ArgumentNullException(nameof(parsers));
TrackTrivia = trackTrivia;
parserStateCache = new BlockParserStateCache(this);
Document = document;
Setup(document, parsers, context, trackTrivia);

document.IsOpen = true;
Parsers = parsers;
Context = context;
OpenedBlocks = new List<Block>();
NewBlocks = new Stack<Block>();
root = this;
Open(document);
}

private BlockProcessor() { }

public bool SkipFirstUnwindSpace { get; set; }

/// <summary>
/// Gets the new blocks to push. A <see cref="BlockParser"/> is required to push new blocks that it creates to this property.
/// </summary>
public Stack<Block> NewBlocks { get; }
public Stack<Block> NewBlocks { get; } = new();

/// <summary>
/// Gets the list of <see cref="BlockParser"/> configured with this parser state.
/// Gets the list of <see cref="BlockParser"/>s configured with this parser state.
/// </summary>
public BlockParserList Parsers { get; }
public BlockParserList Parsers { get; private set; } = null!; // Set in Setup

/// <summary>
/// Gets the parser context or <c>null</c> if none is available.
/// </summary>
public MarkdownParserContext? Context { get; }
public MarkdownParserContext? Context { get; private set; }

/// <summary>
/// Gets the current active container.
Expand All @@ -100,7 +79,7 @@ public BlockProcessor(MarkdownDocument document, BlockParserList parsers, Markdo
/// <summary>
/// Gets the root document.
/// </summary>
public MarkdownDocument Document { get; }
public MarkdownDocument Document { get; private set; } = null!; // Set in Setup

/// <summary>
/// The current line being processed.
Expand Down Expand Up @@ -156,7 +135,7 @@ public BlockProcessor(MarkdownDocument document, BlockParserList parsers, Markdo
/// Gets the character position before the indent occurred.
/// </summary>
public int StartBeforeIndent { get; private set; }

/// <summary>
/// Gets a boolean indicating whether the current line being parsed is lazy continuation.
/// </summary>
Expand All @@ -165,7 +144,7 @@ public BlockProcessor(MarkdownDocument document, BlockParserList parsers, Markdo
/// <summary>
/// Gets the current stack of <see cref="Block"/> being processed.
/// </summary>
private List<Block> OpenedBlocks { get; }
private List<Block> OpenedBlocks { get; } = new();

private bool ContinueProcessingLine { get; set; }

Expand Down Expand Up @@ -212,7 +191,7 @@ internal List<StringSlice> UseLinesBefore()
/// True to parse trivia such as whitespace, extra heading characters and unescaped
/// string values.
/// </summary>
public bool TrackTrivia { get; }
public bool TrackTrivia { get; private set; }

/// <summary>
/// Get the current Container that is currently opened
Expand Down Expand Up @@ -519,21 +498,6 @@ public void ProcessLine(StringSlice newLine)
LineIndex++;
}

public BlockProcessor CreateChild()
{
var newState = parserStateCache.Get();
return newState;
}

public void ReleaseChild()
{
if (this == root)
{
ThrowHelper.InvalidOperationException("Cannot release the root parser state");
}
parserStateCache.Release(this);
}

internal bool IsOpen(Block block)
{
return OpenedBlocks.Contains(block);
Expand Down Expand Up @@ -990,34 +954,73 @@ private void ResetLine(StringSlice newLine)
TriviaStart = newLine.Start;
}



[MemberNotNull(nameof(Document), nameof(Parsers))]
internal void Setup(MarkdownDocument document, BlockParserList parsers, MarkdownParserContext? context, bool trackTrivia)
{
if (document is null) ThrowHelper.ArgumentNullException(nameof(document));
if (parsers is null) ThrowHelper.ArgumentNullException(nameof(parsers));

Document = document;
Parsers = parsers;
Context = context;
TrackTrivia = trackTrivia;
}

private void Reset()
{
Line = StringSlice.Empty;
Column = 0;
Document = null!;
Parsers = null!;
Context = null;
CurrentContainer = null;
CurrentBlock = null;
LastBlock = null;

TrackTrivia = false;
SkipFirstUnwindSpace = false;
ContinueProcessingLine = false;
IsLazy = false;

currentStackIndex = 0;
originalLineStart = 0;
CurrentLineStartPosition = 0;
ColumnBeforeIndent = 0;
StartBeforeIndent = 0;
OpenedBlocks.Clear();
LineIndex = 0;
Column = 0;
TriviaStart = 0;

Line = StringSlice.Empty;

NewBlocks.Clear();
OpenedBlocks.Clear();
LinesBefore = null;
}

private sealed class BlockParserStateCache : ObjectCache<BlockProcessor>
public BlockProcessor CreateChild() => Rent(Document, Parsers, Context, TrackTrivia);

public void ReleaseChild() => Release(this);

private static readonly BlockProcessorCache _cache = new();

internal static BlockProcessor Rent(MarkdownDocument document, BlockParserList parsers, MarkdownParserContext? context, bool trackTrivia)
{
private readonly BlockProcessor root;
var processor = _cache.Get();
processor.Setup(document, parsers, context, trackTrivia);
return processor;
}

public BlockParserStateCache(BlockProcessor root)
{
this.root = root;
}
internal static void Release(BlockProcessor processor)
{
_cache.Release(processor);
}

protected override BlockProcessor NewInstance()
{
return new BlockProcessor(root);
}
private sealed class BlockProcessorCache : ObjectCache<BlockProcessor>
{
protected override BlockProcessor NewInstance() => new BlockProcessor();

protected override void Reset(BlockProcessor instance)
{
instance.Reset();
}
protected override void Reset(BlockProcessor instance) => instance.Reset();
}
}
}

0 comments on commit 7ad7b55

Please sign in to comment.