From c3c4d37b82edcfb531ad6cced5569d398ae0b6d4 Mon Sep 17 00:00:00 2001 From: MihaZupan Date: Thu, 30 Jan 2020 21:14:06 +0100 Subject: [PATCH] Optimize LineReader.ReadLine --- src/Markdig.Tests/TestLineReader.cs | 62 +++++++++++++-------------- src/Markdig/Helpers/LineReader.cs | 49 +++++++++------------ src/Markdig/Parsers/MarkdownParser.cs | 4 +- 3 files changed, 54 insertions(+), 61 deletions(-) diff --git a/src/Markdig.Tests/TestLineReader.cs b/src/Markdig.Tests/TestLineReader.cs index 45fbaea7a..371bd3de8 100644 --- a/src/Markdig.Tests/TestLineReader.cs +++ b/src/Markdig.Tests/TestLineReader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Alexandre Mutel. All rights reserved. +// Copyright (c) Alexandre Mutel. All rights reserved. // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. @@ -18,60 +18,60 @@ public class TestLineReader public void TestEmpty() { var lineReader = new LineReader(""); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestLinesOnlyLf() { var lineReader = new LineReader("\n\n\n"); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); Assert.AreEqual(1, lineReader.SourcePosition); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); Assert.AreEqual(2, lineReader.SourcePosition); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestLinesOnlyCr() { var lineReader = new LineReader("\r\r\r"); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); Assert.AreEqual(1, lineReader.SourcePosition); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); Assert.AreEqual(2, lineReader.SourcePosition); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestLinesOnlyCrLf() { var lineReader = new LineReader("\r\n\r\n\r\n"); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); Assert.AreEqual(2, lineReader.SourcePosition); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); Assert.AreEqual(4, lineReader.SourcePosition); - Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestNoEndOfLine() { var lineReader = new LineReader("123"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestLf() { var lineReader = new LineReader("123\n"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); Assert.AreEqual(4, lineReader.SourcePosition); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] @@ -79,29 +79,29 @@ public void TestLf2() { // When limited == true, we limit the internal buffer exactly after the first new line char '\n' var lineReader = new LineReader("123\n456"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); Assert.AreEqual(4, lineReader.SourcePosition); - Assert.AreEqual("456", lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual("456", lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestCr() { var lineReader = new LineReader("123\r"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); Assert.AreEqual(4, lineReader.SourcePosition); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestCr2() { var lineReader = new LineReader("123\r456"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); Assert.AreEqual(4, lineReader.SourcePosition); - Assert.AreEqual("456", lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual("456", lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] @@ -110,19 +110,19 @@ public void TestCrLf() // When limited == true, we limit the internal buffer exactly after the first new line char '\r' // and we check that we don't get a new line for `\n` var lineReader = new LineReader("123\r\n"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); Assert.AreEqual(5, lineReader.SourcePosition); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.Null(lineReader.ReadLine().Text); } [Test] public void TestCrLf2() { var lineReader = new LineReader("123\r\n456"); - Assert.AreEqual("123", lineReader.ReadLine()?.ToString()); + Assert.AreEqual("123", lineReader.ReadLine().ToString()); Assert.AreEqual(5, lineReader.SourcePosition); - Assert.AreEqual("456", lineReader.ReadLine()?.ToString()); - Assert.Null(lineReader.ReadLine()?.ToString()); + Assert.AreEqual("456", lineReader.ReadLine().ToString()); + Assert.Null(lineReader.ReadLine().Text); } } } \ No newline at end of file diff --git a/src/Markdig/Helpers/LineReader.cs b/src/Markdig/Helpers/LineReader.cs index a5e60a1f5..bc4db7f61 100644 --- a/src/Markdig/Helpers/LineReader.cs +++ b/src/Markdig/Helpers/LineReader.cs @@ -1,10 +1,9 @@ -// Copyright (c) Alexandre Mutel. All rights reserved. +// Copyright (c) Alexandre Mutel. All rights reserved. // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using System; using System.IO; -using System.Text; namespace Markdig.Helpers { @@ -13,17 +12,16 @@ namespace Markdig.Helpers /// public struct LineReader { - private readonly string text; + private readonly string _text; /// /// Initializes a new instance of the class. /// - /// - /// bufferSize cannot be <= 0 + /// + /// bufferSize cannot be <= 0 public LineReader(string text) { - if (text == null) throw new ArgumentNullException(nameof(text)); - this.text = text; + _text = text ?? throw new ArgumentNullException(nameof(text)); SourcePosition = 0; } @@ -36,36 +34,31 @@ public LineReader(string text) /// Reads a new line from the underlying and update the for the next line. /// /// A new line or null if the end of has been reached - public StringSlice? ReadLine() + public StringSlice ReadLine() { - if (SourcePosition >= text.Length) - { - return null; - } + string text = _text; + int sourcePosition = SourcePosition; - var startPosition = SourcePosition; - var position = SourcePosition; - var slice = new StringSlice(text, startPosition, startPosition); - for (;position < text.Length; position++) + for (int i = sourcePosition; i < text.Length; i++) { - var c = text[position]; + char c = text[i]; if (c == '\r' || c == '\n') { - slice.End = position - 1; - if (c == '\r' && position + 1 < text.Length && text[position + 1] == '\n') - { - position++; - } - position++; - SourcePosition = position; + var slice = new StringSlice(text, sourcePosition, i - 1); + + if (c == '\r' && (uint)(i + 1) < (uint)text.Length && text[i + 1] == '\n') + i++; + + SourcePosition = i + 1; return slice; } } - slice.End = position - 1; - SourcePosition = position; + if (sourcePosition >= text.Length) + return default; - return slice; + SourcePosition = int.MaxValue; + return new StringSlice(text, sourcePosition, text.Length - 1); } - } + } } \ No newline at end of file diff --git a/src/Markdig/Parsers/MarkdownParser.cs b/src/Markdig/Parsers/MarkdownParser.cs index 25a9ecfab..6bffeb238 100644 --- a/src/Markdig/Parsers/MarkdownParser.cs +++ b/src/Markdig/Parsers/MarkdownParser.cs @@ -116,11 +116,11 @@ private void ProcessBlocks() var lineText = lineReader.ReadLine(); // If this is the end of file and the last line is empty - if (lineText == null) + if (lineText.Text is null) { break; } - blockProcessor.ProcessLine(lineText.Value); + blockProcessor.ProcessLine(lineText); } blockProcessor.CloseAll(true); }