diff --git a/src/Markdig.Tests/Markdig.Tests.csproj b/src/Markdig.Tests/Markdig.Tests.csproj index 949c757e8..281fdf292 100644 --- a/src/Markdig.Tests/Markdig.Tests.csproj +++ b/src/Markdig.Tests/Markdig.Tests.csproj @@ -1,7 +1,7 @@ - net451;netcoreapp2.1 + netcoreapp2.1;netcoreapp3.1 Library false diff --git a/src/Markdig.Tests/TestDescendantsOrder.cs b/src/Markdig.Tests/TestDescendantsOrder.cs index 2f82bf5cb..996e6b838 100644 --- a/src/Markdig.Tests/TestDescendantsOrder.cs +++ b/src/Markdig.Tests/TestDescendantsOrder.cs @@ -1,9 +1,9 @@ using NUnit.Framework; using Markdig.Syntax; using Markdig.Syntax.Inlines; +using System; using System.Linq; using System.Collections.Generic; -using Markdig.Helpers; namespace Markdig.Tests { @@ -33,9 +33,9 @@ public void TestSchemas() foreach (LiteralInline literalInline in syntaxTree.Descendants()) { - Assert.AreSame(ArrayHelper.Empty, literalInline.Descendants()); - Assert.AreSame(ArrayHelper.Empty, literalInline.Descendants()); - Assert.AreSame(ArrayHelper.Empty, literalInline.Descendants()); + Assert.AreSame(Array.Empty(), literalInline.Descendants()); + Assert.AreSame(Array.Empty(), literalInline.Descendants()); + Assert.AreSame(Array.Empty(), literalInline.Descendants()); } foreach (ContainerInline containerInline in syntaxTree.Descendants()) @@ -50,13 +50,13 @@ public void TestSchemas() if (containerInline.FirstChild is null) { - Assert.AreSame(ArrayHelper.Empty, containerInline.Descendants()); - Assert.AreSame(ArrayHelper.Empty, containerInline.FindDescendants()); - Assert.AreSame(ArrayHelper.Empty, (containerInline as MarkdownObject).Descendants()); + Assert.AreSame(Array.Empty(), containerInline.Descendants()); + Assert.AreSame(Array.Empty(), containerInline.FindDescendants()); + Assert.AreSame(Array.Empty(), (containerInline as MarkdownObject).Descendants()); } - Assert.AreSame(ArrayHelper.Empty, containerInline.Descendants()); - Assert.AreSame(ArrayHelper.Empty, containerInline.Descendants()); + Assert.AreSame(Array.Empty(), containerInline.Descendants()); + Assert.AreSame(Array.Empty(), containerInline.Descendants()); } foreach (ParagraphBlock paragraphBlock in syntaxTree.Descendants()) @@ -65,7 +65,7 @@ public void TestSchemas() (paragraphBlock as MarkdownObject).Descendants(), paragraphBlock.Descendants()); - Assert.AreSame(ArrayHelper.Empty, paragraphBlock.Descendants()); + Assert.AreSame(Array.Empty(), paragraphBlock.Descendants()); } foreach (ContainerBlock containerBlock in syntaxTree.Descendants()) @@ -80,9 +80,9 @@ public void TestSchemas() if (containerBlock.Count == 0) { - Assert.AreSame(ArrayHelper.Empty, containerBlock.Descendants()); - Assert.AreSame(ArrayHelper.Empty, (containerBlock as Block).Descendants()); - Assert.AreSame(ArrayHelper.Empty, (containerBlock as MarkdownObject).Descendants()); + Assert.AreSame(Array.Empty(), containerBlock.Descendants()); + Assert.AreSame(Array.Empty(), (containerBlock as Block).Descendants()); + Assert.AreSame(Array.Empty(), (containerBlock as MarkdownObject).Descendants()); } } } diff --git a/src/Markdig/Extensions/Abbreviations/AbbreviationHelper.cs b/src/Markdig/Extensions/Abbreviations/AbbreviationHelper.cs index faed08a5e..b8a92e480 100644 --- a/src/Markdig/Extensions/Abbreviations/AbbreviationHelper.cs +++ b/src/Markdig/Extensions/Abbreviations/AbbreviationHelper.cs @@ -1,8 +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.Collections.Generic; +using Markdig.Helpers; using Markdig.Syntax; namespace Markdig.Extensions.Abbreviations @@ -21,9 +22,9 @@ public static bool HasAbbreviations(this MarkdownDocument document) public static void AddAbbreviation(this MarkdownDocument document, string label, Abbreviation abbr) { - if (document == null) throw new ArgumentNullException(nameof(document)); - if (label == null) throw new ArgumentNullException(nameof(label)); - if (abbr == null) throw new ArgumentNullException(nameof(abbr)); + if (document == null) ThrowHelper.ArgumentNullException(nameof(document)); + if (label == null) ThrowHelper.ArgumentNullException_label(); + if (abbr == null) ThrowHelper.ArgumentNullException(nameof(abbr)); var map = document.GetAbbreviations(); if (map == null) diff --git a/src/Markdig/Extensions/Abbreviations/AbbreviationParser.cs b/src/Markdig/Extensions/Abbreviations/AbbreviationParser.cs index fefe87ccc..213d1c9c4 100644 --- a/src/Markdig/Extensions/Abbreviations/AbbreviationParser.cs +++ b/src/Markdig/Extensions/Abbreviations/AbbreviationParser.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; using Markdig.Helpers; using Markdig.Parsers; @@ -115,7 +116,7 @@ private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inli ValidAbbreviationStart:; - if (prefixTree.TryMatchLongest(text, i, content.End - i + 1, out KeyValuePair abbreviationMatch)) + if (prefixTree.TryMatchLongest(text.AsSpan(i, content.End - i + 1), out KeyValuePair abbreviationMatch)) { var match = abbreviationMatch.Key; if (!IsValidAbbreviationEnding(match, content, i)) diff --git a/src/Markdig/Extensions/Emoji/EmojiMapping.cs b/src/Markdig/Extensions/Emoji/EmojiMapping.cs index 0f4fc1c29..11232a94c 100644 --- a/src/Markdig/Extensions/Emoji/EmojiMapping.cs +++ b/src/Markdig/Extensions/Emoji/EmojiMapping.cs @@ -1747,10 +1747,10 @@ public EmojiMapping(bool enableSmileys = true) public EmojiMapping(IDictionary shortcodeToUnicode, IDictionary smileyToShortcode) { if (shortcodeToUnicode == null) - throw new ArgumentNullException(nameof(shortcodeToUnicode)); + ThrowHelper.ArgumentNullException(nameof(shortcodeToUnicode)); if (smileyToShortcode == null) - throw new ArgumentNullException(nameof(smileyToShortcode)); + ThrowHelper.ArgumentNullException(nameof(smileyToShortcode)); // Build emojis and smileys CompactPrefixTree @@ -1768,7 +1768,7 @@ public EmojiMapping(IDictionary shortcodeToUnicode, IDictionary< foreach (var shortcode in shortcodeToUnicode) { if (string.IsNullOrEmpty(shortcode.Key) || string.IsNullOrEmpty(shortcode.Value)) - throw new ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(shortcodeToUnicode)); + ThrowHelper.ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(shortcodeToUnicode)); firstChars.Add(shortcode.Key[0]); PrefixTree.Add(shortcode); @@ -1777,15 +1777,15 @@ public EmojiMapping(IDictionary shortcodeToUnicode, IDictionary< foreach (var smiley in smileyToShortcode) { if (string.IsNullOrEmpty(smiley.Key) || string.IsNullOrEmpty(smiley.Value)) - throw new ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(smileyToShortcode)); + ThrowHelper.ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(smileyToShortcode)); if (!shortcodeToUnicode.TryGetValue(smiley.Value, out string unicode)) - throw new ArgumentException(string.Format("Invalid smiley target: {0} is not present in the emoji shortcodes dictionary", smiley.Value)); + ThrowHelper.ArgumentException(string.Format("Invalid smiley target: {0} is not present in the emoji shortcodes dictionary", smiley.Value)); firstChars.Add(smiley.Key[0]); if (!PrefixTree.TryAdd(smiley.Key, unicode)) - throw new ArgumentException(string.Format("Smiley {0} is already present in the emoji mapping", smiley.Key)); + ThrowHelper.ArgumentException(string.Format("Smiley {0} is already present in the emoji mapping", smiley.Key)); } OpeningCharacters = new List(firstChars).ToArray(); diff --git a/src/Markdig/Extensions/Emoji/EmojiParser.cs b/src/Markdig/Extensions/Emoji/EmojiParser.cs index 444fa5cbd..72cb10217 100644 --- a/src/Markdig/Extensions/Emoji/EmojiParser.cs +++ b/src/Markdig/Extensions/Emoji/EmojiParser.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using Markdig.Helpers; using Markdig.Parsers; @@ -34,7 +35,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) } // Try to match an emoji shortcode or smiley - if (!_emojiMapping.PrefixTree.TryMatchLongest(slice.Text, slice.Start, slice.Length, out KeyValuePair match)) + if (!_emojiMapping.PrefixTree.TryMatchLongest(slice.Text.AsSpan(slice.Start, slice.Length), out KeyValuePair match)) { return false; } diff --git a/src/Markdig/Extensions/ListExtras/ListExtraItemParser.cs b/src/Markdig/Extensions/ListExtras/ListExtraItemParser.cs index e19590297..74d308878 100644 --- a/src/Markdig/Extensions/ListExtras/ListExtraItemParser.cs +++ b/src/Markdig/Extensions/ListExtras/ListExtraItemParser.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. @@ -45,16 +45,16 @@ public override bool TryParse(BlockProcessor state, char pendingBulletType, out if ((isRomanLow || isRomanUp) && (pendingBulletType == '\0' || pendingBulletType == 'i' || pendingBulletType == 'I')) { int startChar = state.Start; - int endChar = 0; // With a roman, we can have multiple characters // Note that we don't validate roman numbers - while (isRomanLow ? CharHelper.IsRomanLetterLowerPartial(c) : CharHelper.IsRomanLetterUpperPartial(c)) + do { - endChar = state.Start; c = state.NextChar(); } + while (isRomanLow ? CharHelper.IsRomanLetterLowerPartial(c) : CharHelper.IsRomanLetterUpperPartial(c)); - result.OrderedStart = CharHelper.RomanToArabic(state.Line.Text.Substring(startChar, endChar - startChar + 1)).ToString(); + int orderValue = CharHelper.RomanToArabic(state.Line.Text.AsSpan(startChar, state.Start - startChar)); + result.OrderedStart = CharHelper.SmallNumberToString(orderValue); result.BulletType = isRomanLow ? 'i' : 'I'; result.DefaultOrderedStart = isRomanLow ? "i" : "I"; } @@ -62,7 +62,7 @@ public override bool TryParse(BlockProcessor state, char pendingBulletType, out { // otherwise we expect a regular alpha lettered list with a single character. var isUpper = c.IsAlphaUpper(); - result.OrderedStart = (Char.ToUpperInvariant(c) - 64).ToString(); + result.OrderedStart = CharHelper.SmallNumberToString((c | 0x20) - 'a' + 1); result.BulletType = isUpper ? 'A' : 'a'; result.DefaultOrderedStart = isUpper ? "A" : "a"; state.NextChar(); diff --git a/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs b/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs index 9a1f35b1c..71d99aabc 100644 --- a/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs +++ b/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs @@ -2,6 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using Markdig.Helpers; using System; using System.Collections.Generic; using System.Linq; @@ -40,9 +41,10 @@ public bool TryHandle(Uri mediaUri, bool isSchemaRelative, out string iframeUrl) public static IHostProvider Create(string hostPrefix, Func handler, bool allowFullScreen = true, string iframeClass = null) { if (string.IsNullOrEmpty(hostPrefix)) - throw new ArgumentException("hostPrefix is null or empty.", nameof(hostPrefix)); + ThrowHelper.ArgumentException("hostPrefix is null or empty.", nameof(hostPrefix)); if (handler == null) - throw new ArgumentNullException(nameof(handler)); + ThrowHelper.ArgumentNullException(nameof(handler)); + return new DelegateProvider { HostPrefix = hostPrefix, Delegate = handler, AllowFullScreen = allowFullScreen, Class = iframeClass }; } diff --git a/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs b/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs index f3bc18808..d99e70e0c 100644 --- a/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs +++ b/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs @@ -1,8 +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 Markdig.Helpers; using Markdig.Renderers; namespace Markdig.Extensions.SelfPipeline @@ -29,7 +30,7 @@ public SelfPipelineExtension(string tag = null, string defaultExtensions = null) tag = string.IsNullOrEmpty(tag) ? DefaultTag : tag; if (tag.IndexOfAny(new []{'<', '>'}) >= 0) { - throw new ArgumentException("Tag cannot contain `<` or `>` characters", nameof(tag)); + ThrowHelper.ArgumentException("Tag cannot contain `<` or `>` characters", nameof(tag)); } if (defaultExtensions != null) @@ -57,7 +58,7 @@ public void Setup(MarkdownPipelineBuilder pipeline) // Make sure that this pipeline has only one extension (itself) if (pipeline.Extensions.Count > 1) { - throw new InvalidOperationException( + ThrowHelper.InvalidOperationException( "The SelfPipeline extension cannot be configured with other extensions"); } } @@ -74,7 +75,7 @@ public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) /// public MarkdownPipeline CreatePipelineFromInput(string inputText) { - if (inputText == null) throw new ArgumentNullException(nameof(inputText)); + if (inputText == null) ThrowHelper.ArgumentNullException(nameof(inputText)); var builder = new MarkdownPipelineBuilder(); string defaultConfig = DefaultExtensions; diff --git a/src/Markdig/Extensions/Tables/PipeTableParser.cs b/src/Markdig/Extensions/Tables/PipeTableParser.cs index 88f1a878b..b02abe9c8 100644 --- a/src/Markdig/Extensions/Tables/PipeTableParser.cs +++ b/src/Markdig/Extensions/Tables/PipeTableParser.cs @@ -30,8 +30,7 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor /// The options. public PipeTableParser(LineBreakInlineParser lineBreakParser, PipeTableOptions options = null) { - if (lineBreakParser == null) throw new ArgumentNullException(nameof(lineBreakParser)); - this.lineBreakParser = lineBreakParser; + this.lineBreakParser = lineBreakParser ?? throw new ArgumentNullException(nameof(lineBreakParser)); OpeningCharacters = new[] { '|', '\n' }; Options = options ?? new PipeTableOptions(); } diff --git a/src/Markdig/Helpers/ArrayHelper.cs b/src/Markdig/Helpers/ArrayHelper.cs deleted file mode 100644 index 9ec8ff0f1..000000000 --- a/src/Markdig/Helpers/ArrayHelper.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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. -namespace Markdig.Helpers -{ - /// - /// Helper class for defining Empty arrays. - /// - /// Type of an element of the array - public static class ArrayHelper - { - /// - /// An empty array. - /// - public static readonly T[] Empty = new T[0]; - } -} \ No newline at end of file diff --git a/src/Markdig/Helpers/CharHelper.cs b/src/Markdig/Helpers/CharHelper.cs index fc97bc15e..31f5cd10e 100644 --- a/src/Markdig/Helpers/CharHelper.cs +++ b/src/Markdig/Helpers/CharHelper.cs @@ -2,36 +2,6 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. - -// The IsHighSurrogate, IsLowSurrogate and ConvertToUtf32 methods are copied from -// .Net Core source code which is under MIT license. They are copied here because -// they don't exist in `portable40-net40+sl5+win8+wp8+wpa81`, We probably should remove them -// once we dropped support for that target platform and use the official .Net methods. - -//The MIT License(MIT) - -//Copyright(c) .NET Foundation and Contributors - -//All rights reserved. - -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions: - -//The above copyright notice and this permission notice shall be included in all -//copies or substantial portions of the Software. - -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//SOFTWARE. - using System; using System.Globalization; using System.Collections.Generic; @@ -47,20 +17,20 @@ public static class CharHelper { public const int TabSize = 4; - public const char ZeroSafeChar = '\uFFFD'; + public const char ReplacementChar = '\uFFFD'; - public const string ZeroSafeString = "\uFFFD"; + public const string ReplacementCharString = "\uFFFD"; private const char HighSurrogateStart = '\ud800'; private const char HighSurrogateEnd = '\udbff'; private const char LowSurrogateStart = '\udc00'; private const char LowSurrogateEnd = '\udfff'; - // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. - private const int UnicodePlane01Start = 0x10000; - // We don't support LCDM - private static readonly Dictionary romanMap = new Dictionary { { 'I', 1 }, { 'V', 5 }, { 'X', 10 } }; + private static readonly Dictionary romanMap = new Dictionary(6) { + { 'i', 1 }, { 'v', 5 }, { 'x', 10 }, + { 'I', 1 }, { 'V', 5 }, { 'X', 10 } + }; private static readonly char[] punctuationExceptions = { '−', '-', '†', '‡' }; @@ -104,36 +74,35 @@ public static void CheckOpenCloseDelimiter(char pc, char c, bool enableWithinWor } } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsRomanLetterPartial(char c) { // We don't support LCDM return IsRomanLetterLowerPartial(c) || IsRomanLetterUpperPartial(c); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsRomanLetterLowerPartial(char c) { // We don't support LCDM return c == 'i' || c == 'v' || c == 'x'; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsRomanLetterUpperPartial(char c) { // We don't support LCDM return c == 'I' || c == 'V' || c == 'X'; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] - public static int RomanToArabic(string text) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RomanToArabic(ReadOnlySpan text) { int result = 0; for (int i = 0; i < text.Length; i++) { - var character = char.ToUpperInvariant(text[i]); - var candidate = romanMap[character]; - if (i + 1 < text.Length && candidate < romanMap[char.ToUpperInvariant(text[i + 1])]) + var candidate = romanMap[text[i]]; + if ((uint)(i + 1) < text.Length && candidate < romanMap[text[i + 1]]) { result -= candidate; } @@ -145,7 +114,7 @@ public static int RomanToArabic(string text) return result; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int AddTab(int column) { // return ((column + TabSize) / TabSize) * TabSize; @@ -153,18 +122,18 @@ public static int AddTab(int column) return TabSize + (column & ~(TabSize - 1)); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAcrossTab(int column) { return (column & (TabSize - 1)) != 0; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Contains(this char[] charList, char c) { - for (int i = 0; i < charList.Length; i++) + foreach (char ch in charList) { - if (charList[i] == c) + if (ch == c) { return true; } @@ -172,7 +141,7 @@ public static bool Contains(this char[] charList, char c) return false; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsWhitespace(this char c) { // 2.1 Characters and lines @@ -180,20 +149,20 @@ public static bool IsWhitespace(this char c) return c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsControl(this char c) { return c < ' ' || char.IsControl(c); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEscapableSymbol(this char c) { // char.IsSymbol also works with Unicode symbols that cannot be escaped based on the specification. return (c > ' ' && c < '0') || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || (c > 'z' && c < 127) || c == '•'; } - //[MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + //[MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsWhiteSpaceOrZero(this char c) { return IsZero(c) || IsWhitespace(c); @@ -253,19 +222,19 @@ internal static bool IsSpaceOrPunctuation(this char c) } } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNewLine(this char c) { return c == '\n'; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsZero(this char c) { return c == '\0'; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsSpace(this char c) { // 2.1 Characters and lines @@ -273,7 +242,7 @@ public static bool IsSpace(this char c) return c == ' '; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsTab(this char c) { // 2.1 Characters and lines @@ -281,13 +250,13 @@ public static bool IsTab(this char c) return c == '\t'; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsSpaceOrTab(this char c) { return IsSpace(c) || IsTab(c); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static char EscapeInsecure(this char c) { // 2.3 Insecure characters @@ -295,25 +264,25 @@ public static char EscapeInsecure(this char c) return c == '\0' ? '\ufffd' : c; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAlphaUpper(this char c) { return (uint)(c - 'A') <= ('Z' - 'A'); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAlpha(this char c) { return (uint)((c - 'A') & ~0x20) <= ('Z' - 'A'); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAlphaNumeric(this char c) { return IsAlpha(c) || IsDigit(c); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsDigit(this char c) { return (uint)(c - '0') <= ('9' - '0'); @@ -362,40 +331,31 @@ public static bool IsAsciiPunctuation(this char c) return false; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEmailUsernameSpecialChar(char c) { return ".!#$%&'*+/=?^_`{|}~-+.~".IndexOf(c) >= 0; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsHighSurrogate(char c) { return IsInInclusiveRange(c, HighSurrogateStart, HighSurrogateEnd); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLowSurrogate(char c) { return IsInInclusiveRange(c, LowSurrogateStart, LowSurrogateEnd); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsInInclusiveRange(char c, char min, char max) - => (uint) (c - min) <= (uint) (max - min); + => (uint)(c - min) <= (uint)(max - min); - public static int ConvertToUtf32(char highSurrogate, char lowSurrogate) - { - if (!IsHighSurrogate(highSurrogate)) - { - throw new ArgumentOutOfRangeException(nameof(highSurrogate), "Invalid high surrogate"); - } - if (!IsLowSurrogate(lowSurrogate)) - { - throw new ArgumentOutOfRangeException(nameof(lowSurrogate), "Invalid low surrogate"); - } - return (((highSurrogate - HighSurrogateStart) * 0x400) + (lowSurrogate - LowSurrogateStart) + UnicodePlane01Start); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsInInclusiveRange(int value, uint min, uint max) + => ((uint)value - min) <= (max - min); public static IEnumerable ToUtf32(StringSlice text) { @@ -785,6 +745,24 @@ public static bool IsLeftToRight(int c) c == 0x002128 || c == 0x002395 || c == 0x01D4A2 || c == 0x01D4BB || c == 0x01D546; + } + + // Used by ListExtraItemParser to format numbers from 1 - 26 + private static readonly string[] smallNumberStringCache = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", + "20", "21", "22", "23", "24", "25", "26", + }; + + internal static string SmallNumberToString(int number) + { + string[] cache = smallNumberStringCache; + if ((uint)number < (uint)cache.Length) + { + return cache[number]; + } + + return number.ToString(); } } } \ No newline at end of file diff --git a/src/Markdig/Helpers/CharacterMap.cs b/src/Markdig/Helpers/CharacterMap.cs index f618981a5..edff79eeb 100644 --- a/src/Markdig/Helpers/CharacterMap.cs +++ b/src/Markdig/Helpers/CharacterMap.cs @@ -17,8 +17,8 @@ namespace Markdig.Helpers public class CharacterMap where T : class { private readonly T[] asciiMap; - private readonly Dictionary nonAsciiMap; - private readonly BitVector128 isOpeningCharacter; + private readonly Dictionary nonAsciiMap; + private readonly BoolVector128 isOpeningCharacter; /// /// Initializes a new instance of the class. @@ -27,56 +27,43 @@ public class CharacterMap where T : class /// public CharacterMap(IEnumerable> maps) { - if (maps == null) throw new ArgumentNullException(nameof(maps)); + if (maps == null) ThrowHelper.ArgumentNullException(nameof(maps)); var charSet = new HashSet(); int maxChar = 0; foreach (var map in maps) { var openingChar = map.Key; - charSet.Add(openingChar); - if (openingChar < 128 && openingChar > maxChar) + if (openingChar < 128) { - maxChar = openingChar; + maxChar = Math.Max(maxChar, openingChar); } - else if (openingChar >= 128 && nonAsciiMap == null) + else { - // Initialize only if with have an actual non-ASCII opening character - nonAsciiMap = new Dictionary(); + nonAsciiMap ??= new Dictionary(); } } + OpeningCharacters = charSet.ToArray(); Array.Sort(OpeningCharacters); asciiMap = new T[maxChar + 1]; - var isOpeningCharacter = new BitVector128(); foreach (var state in maps) { - var openingChar = state.Key; - T stateByChar; + char openingChar = state.Key; if (openingChar < 128) { - stateByChar = asciiMap[openingChar]; - - if (stateByChar == null) - { - asciiMap[openingChar] = state.Value; - } + asciiMap[openingChar] ??= state.Value; isOpeningCharacter.Set(openingChar); } - else + else if (!nonAsciiMap.ContainsKey(openingChar)) { - if (!nonAsciiMap.TryGetValue(openingChar, out stateByChar)) - { - nonAsciiMap[openingChar] = state.Value; - } + nonAsciiMap[openingChar] = state.Value; } } - - this.isOpeningCharacter = isOpeningCharacter; } /// @@ -89,24 +76,26 @@ public CharacterMap(IEnumerable> maps) /// /// The opening character. /// A list of parsers valid for the specified opening character or null if no parsers registered. - public T this[char openingChar] + public T this[uint openingChar] { - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - T map = null; - if (openingChar < asciiMap.Length) + T[] asciiMap = this.asciiMap; + if (openingChar < (uint)asciiMap.Length) { - map = asciiMap[openingChar]; + return asciiMap[openingChar]; } - else if (nonAsciiMap != null) + else { - nonAsciiMap.TryGetValue(openingChar, out map); + T map = null; + nonAsciiMap?.TryGetValue(openingChar, out map); + return map; } - return map; } } + /// /// Searches for an opening character from a registered parser in the specified string. /// @@ -116,57 +105,111 @@ public T this[char openingChar] /// Index position within the string of the first opening character found in the specified text; if not found, returns -1 public int IndexOfOpeningCharacter(string text, int start, int end) { - var openingChars = isOpeningCharacter; - - unsafe + if (nonAsciiMap is null) { - fixed (char* pText = text) +#if NETCOREAPP3_1 + ref char textRef = ref Unsafe.AsRef(in text.GetPinnableReference()); + for (; start <= end; start++) { - if (nonAsciiMap == null) + if (IntPtr.Size == 4) { - for (int i = start; i <= end; i++) + uint c = Unsafe.Add(ref textRef, start); + if (c < 128 && isOpeningCharacter[c]) { - var c = pText[i]; - if (c < 128 && openingChars[c]) - { - return i; - } + return start; } } else + { + ulong c = Unsafe.Add(ref textRef, start); + if (c < 128 && isOpeningCharacter[c]) + { + return start; + } + } + } +#else + unsafe + { + fixed (char* pText = text) { for (int i = start; i <= end; i++) { - var c = pText[i]; - if (c < 128 ? openingChars[c] : nonAsciiMap.ContainsKey(c)) + char c = pText[i]; + if (c < 128 && isOpeningCharacter[c]) { return i; } } } } +#endif + return -1; } + else + { + return IndexOfOpeningCharacterNonAscii(text, start, end); + } + } + + private int IndexOfOpeningCharacterNonAscii(string text, int start, int end) + { +#if NETCOREAPP3_1 + ref char textRef = ref Unsafe.AsRef(in text.GetPinnableReference()); + for (int i = start; i <= end; i++) + { + char c = Unsafe.Add(ref textRef, i); + if (c < 128 ? isOpeningCharacter[c] : nonAsciiMap.ContainsKey(c)) + { + return i; + } + } +#else + unsafe + { + fixed (char* pText = text) + { + for (int i = start; i <= end; i++) + { + char c = pText[i]; + if (c < 128 ? isOpeningCharacter[c] : nonAsciiMap.ContainsKey(c)) + { + return i; + } + } + } + } +#endif return -1; } + } - internal unsafe struct BitVector128 + internal unsafe struct BoolVector128 + { + private fixed bool values[128]; + + public void Set(char c) { - fixed uint values[4]; + Debug.Assert(c < 128); + values[c] = true; + } - public void Set(char c) + public readonly bool this[uint c] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { Debug.Assert(c < 128); - values[c >> 5] |= (uint)1 << c; + return values[c]; } - - public readonly bool this[char c] + } + public readonly bool this[ulong c] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] - get - { - Debug.Assert(c < 128); - return (values[c >> 5] & (uint)1 << c) != 0; - } + Debug.Assert(c < 128 && IntPtr.Size == 8); + return values[c]; } } } diff --git a/src/Markdig/Helpers/CompactPrefixTree.cs b/src/Markdig/Helpers/CompactPrefixTree.cs index 8c6e067f8..207a1b866 100644 --- a/src/Markdig/Helpers/CompactPrefixTree.cs +++ b/src/Markdig/Helpers/CompactPrefixTree.cs @@ -27,10 +27,7 @@ namespace Markdig.Helpers /// /// The value associated with the key [ExcludeFromCodeCoverage] - internal sealed class CompactPrefixTree -//#if !LEGACY -// : IReadOnlyDictionary, IReadOnlyList> -//#endif + internal sealed class CompactPrefixTree : IReadOnlyDictionary, IReadOnlyList> { /// /// Used internally to control behavior of insertion @@ -120,7 +117,7 @@ public int TreeCapacity } } } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureTreeCapacity(int min) { if (_tree.Length < min) @@ -169,7 +166,7 @@ public int Capacity } } } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureCapacity(int min) { // Expansion logic as in System.Collections.Generic.List @@ -227,7 +224,7 @@ public int ChildrenCapacity } } } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureChildrenCapacity(int min) { if (_children.Length < min) @@ -254,7 +251,7 @@ private void EnsureChildrenCapacityRare(int min) // Inspired by Markdig's CharacterMap - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryGetRoot(char rootChar, out int rootNodeIndex) { if (rootChar < 128) @@ -338,7 +335,7 @@ public CompactPrefixTree(ICollection> input) /// The key/value pair of the element at the specified index public KeyValuePair this[int index] { - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if ((uint)index >= (uint)Count) ThrowHelper.ThrowIndexOutOfRangeException(); @@ -355,7 +352,7 @@ public TValue this[string key] { get { - if (TryMatchExact(key, out KeyValuePair match)) + if (TryMatchExact(key.AsSpan(), out KeyValuePair match)) return match.Value; throw new KeyNotFoundException(key); } @@ -367,7 +364,6 @@ public TValue this[string key] } } // Get, Set -#if NETCORE /// /// Gets the value associated with the specified key /// @@ -382,7 +378,6 @@ public KeyValuePair this[ReadOnlySpan key] throw new KeyNotFoundException(key.ToString()); } } // Get only -#endif #endregion this[] accessors @@ -713,50 +708,6 @@ private void InsertLeafNode(in KeyValuePair pair, char nodeChar) #region TryMatch longest - /// - /// Tries to find the longest prefix of text, starting at offset, that is contained in this - /// - /// The text in which to search for the prefix - /// Index of the character at which to start searching - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise - public bool TryMatchLongest(string text, int offset, out KeyValuePair match) - { -#if NETCORE - return TryMatchLongest(text.AsSpan(offset), out match); -#else - return TryMatchLongest(text, offset, text.Length - offset, out match); -#endif - } - - /// - /// Tries to find the longest prefix of text, that is contained in this - /// - /// The text in which to search for the prefix - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise - public bool TryMatchLongest(string text, out KeyValuePair match) - { - if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); -#if NETCORE - return TryMatchLongest(text.AsSpan(), out match); -#else - return TryMatchLongest(text, 0, text.Length, out match); -#endif - } - - /// - /// Tries to find the longest prefix of text, starting at offset, that is contained in this - /// - /// The text in which to search for the prefix - /// The offset in text at which to start looking for the prefix - /// The longest prefix allowed to match - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise -#if NETCORE - public bool TryMatchLongest(string text, int offset, int length, out KeyValuePair match) - => TryMatchLongest(text.AsSpan(offset, length), out match); - /// /// Tries to find the longest prefix of text, that is contained in this /// @@ -768,20 +719,6 @@ public bool TryMatchLongest(ReadOnlySpan text, out KeyValuePair match) - { - int limit = offset + length; - if (text == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - if (offset < 0 || length < 0 || text.Length < limit) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offsetLength, ExceptionReason.InvalidOffsetLength); - - match = default; - if (length == 0 || !TryGetRoot(text[offset], out int nodeIndex)) - return false; -#endif int matchIndex = -1; int depth = 1; @@ -790,11 +727,7 @@ public bool TryMatchLongest(string text, int offset, int length, out KeyValuePai if (node.ChildChar == 0) goto LeafNodeFound; if (node.MatchIndex != -1) matchIndex = node.MatchIndex; -#if NETCORE for (int i = 1; i < text.Length; i++) -#else - for (int i = offset + 1; i < limit; i++) -#endif { char c = text[i]; @@ -826,7 +759,6 @@ public bool TryMatchLongest(string text, int offset, int length, out KeyValuePai LeafNodeFound:; ref KeyValuePair possibleMatch = ref _matches[node.MatchIndex]; -#if NETCORE if (possibleMatch.Key.Length <= text.Length) { // Check that the rest of the strings match @@ -835,18 +767,6 @@ public bool TryMatchLongest(string text, int offset, int length, out KeyValuePai matchIndex = node.MatchIndex; } } -#else - if (possibleMatch.Key.Length <= length) - { - // Check that the rest of the strings match - for (int i = offset + depth, j = depth; j < possibleMatch.Key.Length; i++, j++) - { - if (text[i] != possibleMatch.Key[j]) - goto Return; - } - matchIndex = node.MatchIndex; - } -#endif Return:; if (matchIndex != -1) @@ -861,50 +781,6 @@ public bool TryMatchLongest(string text, int offset, int length, out KeyValuePai #region TryMatch exact - /// - /// Tries to find a suffix of text, starting at offset, that is contained in this and is exactly (text.Length - offset) characters long - /// - /// The text in which to search for the prefix - /// Index of the character at which to start searching - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise - public bool TryMatchExact(string text, int offset, out KeyValuePair match) - { -#if NETCORE - return TryMatchExact(text.AsSpan(offset), out match); -#else - return TryMatchExact(text, offset, text.Length - offset, out match); -#endif - } - - /// - /// Tries to find a prefix of text, that is contained in this and is exactly text.Length characters long - /// - /// The text in which to search for the prefix - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise - public bool TryMatchExact(string text, out KeyValuePair match) - { - if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); -#if NETCORE - return TryMatchExact(text.AsSpan(), out match); -#else - return TryMatchExact(text, 0, text.Length, out match); -#endif - } - - /// - /// Tries to find a prefix of text, starting at offset, that is contained in this and is exactly length characters long - /// - /// The text in which to search for the prefix - /// The offset in text at which to start looking for the prefix - /// The longest prefix allowed to match - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise -#if NETCORE - public bool TryMatchExact(string text, int offset, int length, out KeyValuePair match) - => TryMatchExact(text.AsSpan(offset, length), out match); - /// /// Tries to find a prefix of text, that is contained in this and is exactly text.Length characters long /// @@ -916,39 +792,18 @@ public bool TryMatchExact(ReadOnlySpan text, out KeyValuePair match) - { - int limit = offset + length; - if (text == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - if (offset < 0 || length < 0 || text.Length < limit) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offsetLength, ExceptionReason.InvalidOffsetLength); - match = default; - if (length == 0 || !TryGetRoot(text[offset], out int nodeIndex)) - return false; -#endif int depth = 1; ref Node node = ref _tree[nodeIndex]; if (node.ChildChar == 0) goto LeafNodeFound; -#if NETCORE if (node.MatchIndex != -1 && text.Length == 1) -#else - if (node.MatchIndex != -1 && length == 1) -#endif { match = _matches[node.MatchIndex]; return true; } -#if NETCORE for (int i = 1; i < text.Length; i++) -#else - for (int i = offset + 1; i < limit; i++) -#endif { char c = text[i]; @@ -977,81 +832,20 @@ public bool TryMatchExact(string text, int offset, int length, out KeyValuePair< if (node.MatchIndex == -1) return false; match = _matches[node.MatchIndex]; -#if NETCORE Debug.Assert(match.Key.Length == text.Length); -#else - Debug.Assert(match.Key.Length == length); -#endif return true; LeafNodeFound:; match = _matches[node.MatchIndex]; -#if NETCORE + return match.Key.Length == text.Length && text.Slice(depth).Equals(match.Key.AsSpan(depth), StringComparison.Ordinal); -#else - if (match.Key.Length == length) - { - // Check that the rest of the strings match - for (int i = offset + depth, j = depth; j < match.Key.Length; i++, j++) - { - if (text[i] != match.Key[j]) - return false; - } - return true; - } - return false; -#endif } #endregion TryMatch exact #region TryMatch shortest - /// - /// Tries to find the shortest prefix of text, starting at offset, that is contained in this - /// - /// The text in which to search for the prefix - /// Index of the character at which to start searching - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise - public bool TryMatchShortest(string text, int offset, out KeyValuePair match) - { -#if NETCORE - return TryMatchShortest(text.AsSpan(offset), out match); -#else - return TryMatchShortest(text, offset, text.Length - offset, out match); -#endif - } - - /// - /// Tries to find the shortest prefix of text, that is contained in this - /// - /// The text in which to search for the prefix - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise - public bool TryMatchShortest(string text, out KeyValuePair match) - { - if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); -#if NETCORE - return TryMatchShortest(text.AsSpan(), out match); -#else - return TryMatchShortest(text, 0, text.Length, out match); -#endif - } - - /// - /// Tries to find the shortest prefix of text, starting at offset, that is contained in this - /// - /// The text in which to search for the prefix - /// The offset in text at which to start looking for the prefix - /// The longest prefix allowed to match - /// The found prefix and the corresponding value - /// True if a match was found, false otherwise -#if NETCORE - public bool TryMatchShortest(string text, int offset, int length, out KeyValuePair match) - => TryMatchShortest(text.AsSpan(offset, length), out match); - /// /// Tries to find the shortest prefix of text, that is contained in this /// @@ -1063,20 +857,7 @@ public bool TryMatchShortest(ReadOnlySpan text, out KeyValuePair match) - { - int limit = offset + length; - if (text == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - if (offset < 0 || length < 0 || text.Length < limit) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offsetLength, ExceptionReason.InvalidOffsetLength); - match = default; - if (length == 0 || !TryGetRoot(text[offset], out int nodeIndex)) - return false; -#endif ref Node node = ref _tree[nodeIndex]; if (node.MatchIndex != -1) { @@ -1084,11 +865,7 @@ public bool TryMatchShortest(string text, int offset, int length, out KeyValuePa return true; } -#if NETCORE for (int i = 1; i < text.Length; i++) -#else - for (int i = offset + 1; i < limit; i++) -#endif { char c = text[i]; @@ -1131,7 +908,7 @@ public bool TryMatchShortest(string text, int offset, int length, out KeyValuePa /// The key to locate in this /// True if the key is contained in this PrefixTree, false otherwise. public bool ContainsKey(string key) - => TryMatchExact(key, out _); + => TryMatchExact(key.AsSpan(), out _); /// /// Gets the value associated with the specified key @@ -1141,7 +918,7 @@ public bool ContainsKey(string key) /// True if the key is contained in this PrefixTree, false otherwise. public bool TryGetValue(string key, out TValue value) { - bool ret = TryMatchExact(key, out KeyValuePair match); + bool ret = TryMatchExact(key.AsSpan(), out KeyValuePair match); value = match.Value; return ret; } @@ -1175,9 +952,8 @@ public IEnumerable Values /// /// public IEnumerator> GetEnumerator() => new Enumerator(_matches); -//#if !LEGACY - //IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_matches); -//#endif + + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_matches); /// /// Enumerates the elements of a diff --git a/src/Markdig/Helpers/EntityHelper.cs b/src/Markdig/Helpers/EntityHelper.cs index 2f1c648fe..4fd7f328a 100644 --- a/src/Markdig/Helpers/EntityHelper.cs +++ b/src/Markdig/Helpers/EntityHelper.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. @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Text; namespace Markdig.Helpers { @@ -46,11 +47,10 @@ public static class EntityHelper /// /// The entity without & and ; symbols, for example, copy. /// The unicode character set or null if the entity was not recognized. - public static string DecodeEntity(string entity) + public static string DecodeEntity(ReadOnlySpan entity) { - string result; - if (EntityMap.TryGetValue(entity, out result)) - return result; + if (EntityMap.TryMatchExact(entity, out KeyValuePair result)) + return result.Value; return null; } @@ -61,25 +61,49 @@ public static string DecodeEntity(string entity) /// The unicode character set or null if the entity was not recognized. public static string DecodeEntity(int utf32) { - if (utf32 < 0 || utf32 > 1114111 || (utf32 >= 55296 && utf32 <= 57343)) - return null; + if (!CharHelper.IsInInclusiveRange(utf32, 1, 1114111) || CharHelper.IsInInclusiveRange(utf32, 55296, 57343)) + return CharHelper.ReplacementCharString; if (utf32 < 65536) return char.ToString((char)utf32); utf32 -= 65536; - return new string(new char[] + return new string( +#if NETCORE + stackalloc +#else + new +#endif + char[] { - (char)(utf32 / 1024 + 55296), - (char)(utf32 % 1024 + 56320) + (char)((uint)utf32 / 1024 + 55296), + (char)((uint)utf32 % 1024 + 56320) }); } - #region [ EntityMap ] + public static void DecodeEntity(int utf32, StringBuilder sb) + { + if (!CharHelper.IsInInclusiveRange(utf32, 1, 1114111) || CharHelper.IsInInclusiveRange(utf32, 55296, 57343)) + { + sb.Append(CharHelper.ReplacementChar); + } + else if (utf32 < 65536) + { + sb.Append((char)utf32); + } + else + { + utf32 -= 65536; + sb.Append((char)((uint)utf32 / 1024 + 55296)); + sb.Append((char)((uint)utf32 % 1024 + 56320)); + } + } + +#region [ EntityMap ] /// /// Source: http://www.w3.org/html/wg/drafts/html/master/syntax.html#named-character-references /// - private static readonly Dictionary EntityMap = new Dictionary(2125, StringComparer.Ordinal) + private static readonly CompactPrefixTree EntityMap = new CompactPrefixTree(2125, 3385, 3510) { { "Aacute", "\u00C1" }, { "aacute", "\u00E1" }, @@ -2207,6 +2231,6 @@ public static string DecodeEntity(int utf32) { "zwj", "\u200D" }, { "zwnj", "\u200C" } }; - #endregion +#endregion } } \ No newline at end of file diff --git a/src/Markdig/Helpers/ExcludeFromCodeCoverageAttribute.cs b/src/Markdig/Helpers/ExcludeFromCodeCoverageAttribute.cs deleted file mode 100644 index ac185b74c..000000000 --- a/src/Markdig/Helpers/ExcludeFromCodeCoverageAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -#if NET35 -namespace System.Diagnostics.CodeAnalysis -{ - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] - internal class ExcludeFromCodeCoverageAttribute : Attribute - { - - } -} -#endif \ No newline at end of file diff --git a/src/Markdig/Helpers/HtmlHelper.cs b/src/Markdig/Helpers/HtmlHelper.cs index d46ac3b39..4d385ad30 100644 --- a/src/Markdig/Helpers/HtmlHelper.cs +++ b/src/Markdig/Helpers/HtmlHelper.cs @@ -51,7 +51,7 @@ public static bool TryParseHtmlTag(ref StringSlice text, out string htmlTag) public static bool TryParseHtmlTag(ref StringSlice text, StringBuilder builder) { - if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (builder == null) ThrowHelper.ArgumentNullException(nameof(builder)); var c = text.CurrentChar; if (c != '<') { @@ -450,11 +450,7 @@ public static string Unescape(string text, bool removeBackSlash = true) while ((searchPos = text.IndexOfAny(search, searchPos)) != -1) { - if (sb == null) - { - sb = StringBuilderCache.Local(); - sb.Length = 0; - } + sb ??= StringBuilderCache.Local(); c = text[searchPos]; if (removeBackSlash && c == '\\') { @@ -472,10 +468,7 @@ public static string Unescape(string text, bool removeBackSlash = true) } else if (c == '&') { - int entityNameStart; - int entityNameLength; - int numericEntity; - var match = ScanEntity(new StringSlice(text, searchPos, text.Length - 1), out numericEntity, out entityNameStart, out entityNameLength); + var match = ScanEntity(new StringSlice(text, searchPos, text.Length - 1), out int numericEntity, out int entityNameStart, out int entityNameLength); if (match == 0) { searchPos++; @@ -486,8 +479,7 @@ public static string Unescape(string text, bool removeBackSlash = true) if (entityNameLength > 0) { - var namedEntity = new StringSlice(text, entityNameStart, entityNameStart + entityNameLength - 1); - var decoded = EntityHelper.DecodeEntity(namedEntity.ToString()); + var decoded = EntityHelper.DecodeEntity(text.AsSpan(entityNameStart, entityNameLength)); if (decoded != null) { sb.Append(text, lastPos, searchPos - match - lastPos); @@ -498,36 +490,18 @@ public static string Unescape(string text, bool removeBackSlash = true) else if (numericEntity >= 0) { sb.Append(text, lastPos, searchPos - match - lastPos); - if (numericEntity == 0) - { - sb.Append('\0'.EscapeInsecure()); - } - else - { - var decoded = EntityHelper.DecodeEntity(numericEntity); - if (decoded != null) - { - sb.Append(decoded); - } - else - { - sb.Append('\uFFFD'); - } - } - + EntityHelper.DecodeEntity(numericEntity, sb); lastPos = searchPos; } } } } - if (sb == null) + if (sb == null || lastPos == 0) return text; sb.Append(text, lastPos, text.Length - lastPos); - var result = sb.ToString(); - sb.Length = 0; - return result; + return sb.GetStringAndReset(); } /// diff --git a/src/Markdig/Helpers/LineReader.cs b/src/Markdig/Helpers/LineReader.cs index bc4db7f61..362eb9622 100644 --- a/src/Markdig/Helpers/LineReader.cs +++ b/src/Markdig/Helpers/LineReader.cs @@ -21,7 +21,10 @@ public struct LineReader /// bufferSize cannot be <= 0 public LineReader(string text) { - _text = text ?? throw new ArgumentNullException(nameof(text)); + if (text is null) + ThrowHelper.ArgumentNullException_text(); + + _text = text; SourcePosition = 0; } diff --git a/src/Markdig/Helpers/LinkHelper.cs b/src/Markdig/Helpers/LinkHelper.cs index 76190154e..7ad4ad9f0 100644 --- a/src/Markdig/Helpers/LinkHelper.cs +++ b/src/Markdig/Helpers/LinkHelper.cs @@ -110,7 +110,7 @@ public static string UrilizeAsGfm(string headingText) return headingBuffer.GetStringAndReset(); } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsReservedPunctuation(char c) { return c == '_' || c == '-' || c == '.'; @@ -652,14 +652,14 @@ public static bool TryParseUrl(ref T text, out string link, bool isAutoLink = return isValid; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsTrailingUrlStopCharacter(char c) { // Trailing punctuation (specifically, ?, !, ., ,, :, *, _, and ~) will not be considered part of the autolink, though they may be included in the interior of the link: return c == '?' || c == '!' || c == '.' || c == ',' || c == ':' || c == '*' || c == '*' || c == '_' || c == '~'; } - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsEndOfUri(char c, bool isAutoLink) { return c == '\0' || c.IsSpaceOrTab() || c.IsControl() || (isAutoLink && c == '<'); // TODO: specs unclear. space is strict or relaxed? (includes tabs?) diff --git a/src/Markdig/Helpers/MethodImplOptionPortable.cs b/src/Markdig/Helpers/MethodImplOptionPortable.cs deleted file mode 100644 index ff65e0a91..000000000 --- a/src/Markdig/Helpers/MethodImplOptionPortable.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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.Runtime.CompilerServices; - -namespace Markdig.Helpers -{ - /// - /// Internal helper to allow to declare a method using AggressiveInlining without being .NET 4.0+ - /// - internal static class MethodImplOptionPortable - { - public const MethodImplOptions AggressiveInlining = (MethodImplOptions)256; - } -} \ No newline at end of file diff --git a/src/Markdig/Helpers/ObjectCache.cs b/src/Markdig/Helpers/ObjectCache.cs index 5f7878f4c..998cb48d3 100644 --- a/src/Markdig/Helpers/ObjectCache.cs +++ b/src/Markdig/Helpers/ObjectCache.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. using System; @@ -57,7 +57,7 @@ public T Get() /// if instance is null public void Release(T instance) { - if (instance == null) throw new ArgumentNullException(nameof(instance)); + if (instance == null) ThrowHelper.ArgumentNullException(nameof(instance)); Reset(instance); lock (builders) { diff --git a/src/Markdig/Helpers/OrderedList.cs b/src/Markdig/Helpers/OrderedList.cs index 3e3663d70..4aa547b68 100644 --- a/src/Markdig/Helpers/OrderedList.cs +++ b/src/Markdig/Helpers/OrderedList.cs @@ -22,85 +22,85 @@ public OrderedList(IEnumerable collection) : base(collection) { } - public bool InsertBefore(T element) where TElement : T + public bool InsertBefore(T item) where TItem : T { - if (element == null) throw new ArgumentNullException(nameof(element)); + if (item == null) ThrowHelper.ArgumentNullException_item(); for (int i = 0; i < Count; i++) { - if (this[i] is TElement) + if (this[i] is TItem) { - Insert(i, element); + Insert(i, item); return true; } } return false; } - public TElement Find() where TElement : T + public TItem Find() where TItem : T { for (int i = 0; i < Count; i++) { - if (this[i] is TElement) + if (this[i] is TItem) { - return (TElement)this[i]; + return (TItem)this[i]; } } return default; } - public bool TryFind(out TElement element) where TElement : T + public bool TryFind(out TItem item) where TItem : T { - element = Find(); - return element != null; + item = Find(); + return item != null; } - public TElement FindExact() where TElement : T + public TItem FindExact() where TItem : T { for (int i = 0; i < Count; i++) { - if (this[i].GetType() == typeof(TElement)) + if (this[i].GetType() == typeof(TItem)) { - return (TElement)this[i]; + return (TItem)this[i]; } } return default; } - public void AddIfNotAlready() where TElement : class, T, new() + public void AddIfNotAlready() where TItem : class, T, new() { - if (!Contains()) + if (!Contains()) { - Add(new TElement()); + Add(new TItem()); } } - public void AddIfNotAlready(TElement telement) where TElement : T + public void AddIfNotAlready(TItem item) where TItem : T { - if (!Contains()) + if (!Contains()) { - Add(telement); + Add(item); } } - public bool InsertAfter(T element) where TElement : T + public bool InsertAfter(T item) where TItem : T { - if (element == null) throw new ArgumentNullException(nameof(element)); + if (item == null) ThrowHelper.ArgumentNullException_item(); for (int i = 0; i < Count; i++) { - if (this[i] is TElement) + if (this[i] is TItem) { - Insert(i + 1, element); + Insert(i + 1, item); return true; } } return false; } - public bool Contains() where TElement : T + public bool Contains() where TItem : T { for (int i = 0; i < Count; i++) { - if (this[i] is TElement) + if (this[i] is TItem) { return true; } @@ -109,16 +109,16 @@ public bool Contains() where TElement : T } /// - /// Replaces with . + /// Replaces with . /// - /// Element type to find in the list - /// Object to replace this element with + /// Item type to find in the list + /// Object to replace this item with /// true if a replacement was made; otherwise false. - public bool Replace(T replacement) where TElement : T + public bool Replace(T replacement) where TItem : T { for (var i = 0; i < Count; i++) { - if (this[i] is TElement) + if (this[i] is TItem) { RemoveAt(i); Insert(i, replacement); @@ -129,28 +129,28 @@ public bool Replace(T replacement) where TElement : T } /// - /// Replaces with or adds . + /// Replaces with or adds . /// - /// Element type to find in the list - /// Object to add/replace the found element with + /// Item type to find in the list + /// Object to add/replace the found item with /// true if a replacement was made; otherwise false. - public bool ReplaceOrAdd(T newElement) where TElement : T + public bool ReplaceOrAdd(T newItem) where TItem : T { - if (Replace(newElement)) + if (Replace(newItem)) return true; - Add(newElement); + Add(newItem); return false; } /// - /// Removes the first occurrence of + /// Removes the first occurrence of /// - public bool TryRemove() where TElement : T + public bool TryRemove() where TItem : T { for (int i = 0; i < Count; i++) { - if (this[i] is TElement) + if (this[i] is TItem) { RemoveAt(i); return true; diff --git a/src/Markdig/Helpers/StringLineGroup.cs b/src/Markdig/Helpers/StringLineGroup.cs index 6752dc978..18a2486bc 100644 --- a/src/Markdig/Helpers/StringLineGroup.cs +++ b/src/Markdig/Helpers/StringLineGroup.cs @@ -26,7 +26,7 @@ private static readonly CustomArrayPool _pool /// public StringLineGroup(int capacity, bool willRelease = false) { - if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + if (capacity <= 0) ThrowHelper.ArgumentOutOfRangeException(nameof(capacity)); Lines = _pool.Rent(willRelease ? Math.Max(8, capacity) : capacity); Count = 0; } @@ -38,7 +38,7 @@ public StringLineGroup(int capacity, bool willRelease = false) /// public StringLineGroup(string text) { - if (text == null) throw new ArgumentNullException(nameof(text)); + if (text == null) ThrowHelper.ArgumentNullException_text(); Lines = new StringLine[1]; Count = 0; Add(new StringSlice(text)); @@ -82,7 +82,7 @@ public void RemoveAt(int index) /// Adds the specified line to this instance. /// /// The line. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(ref StringLine line) { if (Count == Lines.Length) IncreaseCapacity(); @@ -93,7 +93,7 @@ public void Add(ref StringLine line) /// Adds the specified slice to this instance. /// /// The slice. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(StringSlice slice) { if (Count == Lines.Length) IncreaseCapacity(); @@ -283,7 +283,7 @@ public char NextChar() public readonly char PeekChar(int offset = 1) { - if (offset < 0) throw new ArgumentOutOfRangeException("Negative offset are not supported for StringLineGroup", nameof(offset)); + if (offset < 0) ThrowHelper.ArgumentOutOfRangeException("Negative offset are not supported for StringLineGroup", nameof(offset)); if (Start + offset > End) { diff --git a/src/Markdig/Helpers/StringSlice.cs b/src/Markdig/Helpers/StringSlice.cs index 28a96b1f5..1b07e7ea1 100644 --- a/src/Markdig/Helpers/StringSlice.cs +++ b/src/Markdig/Helpers/StringSlice.cs @@ -38,7 +38,10 @@ public StringSlice(string text) /// public StringSlice(string text, int start, int end) { - Text = text ?? throw new ArgumentNullException(nameof(text)); + if (text is null) + ThrowHelper.ArgumentNullException_text(); + + Text = text; Start = start; End = end; } @@ -51,12 +54,12 @@ public StringSlice(string text, int start, int end) /// /// Gets or sets the start position within . /// - public int Start { get; set; } + public int Start { readonly get; set; } /// /// Gets or sets the end position (inclusive) within . /// - public int End { get; set; } + public int End { readonly get; set; } /// /// Gets the length. @@ -80,7 +83,7 @@ public readonly char CurrentChar /// public readonly bool IsEmpty { - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Start > End; } @@ -91,7 +94,7 @@ public readonly bool IsEmpty /// A character in the slice at the specified index (not from but from the begining of the slice) public readonly char this[int index] { - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Text[index]; } @@ -102,7 +105,7 @@ public readonly char this[int index] /// /// The next character. `\0` is end of the iteration. /// - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public char NextChar() { int start = Start; @@ -121,7 +124,7 @@ public char NextChar() /// inside the range and , returns `\0` if outside this range. /// /// The character at offset, returns `\0` if none. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly char PeekChar() { int index = Start + 1; @@ -134,7 +137,7 @@ public readonly char PeekChar() /// /// The offset. /// The character at offset, returns `\0` if none. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly char PeekChar(int offset) { var index = Start + offset; @@ -145,7 +148,7 @@ public readonly char PeekChar(int offset) /// Peeks a character at the specified offset from the current beginning of the string, without taking into account and /// /// The character at offset, returns `\0` if none. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly char PeekCharAbsolute(int index) { string text = Text; @@ -158,7 +161,7 @@ public readonly char PeekCharAbsolute(int index) /// /// The offset. /// The character at offset, returns `\0` if none. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly char PeekCharExtra(int offset) { var index = Start + offset; @@ -275,13 +278,7 @@ public readonly int IndexOf(string text, int offset = 0, bool ignoreCase = false if (length <= 0) return -1; -#if NETCORE - var span = Text.AsSpan(offset, length); - int index = ignoreCase ? span.IndexOf(text, StringComparison.OrdinalIgnoreCase) : span.IndexOf(text); - return index == -1 ? index : index + offset; -#else return Text.IndexOf(text, offset, length, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); -#endif } /// @@ -296,12 +293,7 @@ public readonly int IndexOf(char c) if (length <= 0) return -1; -#if NETCORE - int index = Text.AsSpan(start, length).IndexOf(c); - return index == -1 ? index : index + start; -#else return Text.IndexOf(c, start, length); -#endif } /// @@ -312,16 +304,15 @@ public readonly int IndexOf(char c) /// public bool TrimStart() { - // Strip leading spaces - for (; Start <= End; Start++) - { - if (Start < Text.Length - && !Text[Start].IsWhitespace()) - { - break; - } - } - return IsEmpty; + string text = Text; + int end = End; + int i = Start; + + while (i <= end && (uint)i < (uint)text.Length && text[i].IsWhitespace()) + i++; + + Start = i; + return i > end; } /// @@ -330,16 +321,15 @@ public bool TrimStart() /// The number of spaces trimmed. public void TrimStart(out int spaceCount) { - spaceCount = 0; - // Strip leading spaces - for (; Start <= End; Start++) - { - if (!Text[Start].IsWhitespace()) - { - break; - } - spaceCount++; - } + string text = Text; + int end = End; + int i = Start; + + while (i <= end && (uint)i < (uint)text.Length && text[i].IsWhitespace()) + i++; + + spaceCount = i - Start; + Start = i; } /// @@ -348,15 +338,15 @@ public void TrimStart(out int spaceCount) /// public bool TrimEnd() { - for (; Start <= End; End--) - { - if (End < Text.Length - && !Text[End].IsWhitespace()) - { - break; - } - } - return IsEmpty; + string text = Text; + int start = Start; + int i = End; + + while (start <= i && (uint)i < (uint)text.Length && text[i].IsWhitespace()) + i--; + + End = i; + return start > i; } /// @@ -364,8 +354,18 @@ public bool TrimEnd() /// public void Trim() { - TrimStart(); - TrimEnd(); + string text = Text; + int start = Start; + int end = End; + + while (start <= end && (uint)start < (uint)text.Length && text[start].IsWhitespace()) + start++; + + while (start <= end && (uint)end < (uint)text.Length && text[end].IsWhitespace()) + end--; + + Start = start; + End = end; } /// @@ -393,9 +393,12 @@ public readonly override string ToString() /// true if this slice is empty or made only of whitespaces; false otherwise public readonly bool IsEmptyOrWhitespace() { - for (int i = Start; i <= End; i++) + string text = Text; + int end = End; + + for (int i = Start; i <= end && (uint)i < (uint)text.Length; i++) { - if (!Text[i].IsWhitespace()) + if (!text[i].IsWhitespace()) { return false; } diff --git a/src/Markdig/Helpers/ThrowHelper.cs b/src/Markdig/Helpers/ThrowHelper.cs index d3b884abf..84e509c84 100644 --- a/src/Markdig/Helpers/ThrowHelper.cs +++ b/src/Markdig/Helpers/ThrowHelper.cs @@ -10,19 +10,39 @@ namespace Markdig.Helpers [ExcludeFromCodeCoverage] internal static class ThrowHelper { + public static void ArgumentNullException(string paramName) => throw new ArgumentNullException(paramName); + public static void ArgumentNullException_item() => throw new ArgumentNullException("item"); + public static void ArgumentNullException_text() => throw new ArgumentNullException("text"); + public static void ArgumentNullException_label() => throw new ArgumentNullException("label"); + public static void ArgumentNullException_key() => throw new ArgumentNullException("key"); + public static void ArgumentNullException_name() => throw new ArgumentNullException("name"); + public static void ArgumentNullException_markdown() => throw new ArgumentNullException("markdown"); + public static void ArgumentNullException_writer() => throw new ArgumentNullException("writer"); + public static void ArgumentNullException_leafBlock() => throw new ArgumentNullException("leafBlock"); + public static void ArgumentNullException_markdownObject() => throw new ArgumentNullException("markdownObject"); + + public static void ArgumentException(string message) => throw new ArgumentException(message); + public static void ArgumentException(string message, string paramName) => throw new ArgumentException(message, paramName); + + public static void ArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName); + public static void ArgumentOutOfRangeException(string message, string paramName) => throw new ArgumentOutOfRangeException(message, paramName); + public static void ArgumentOutOfRangeException_index() => throw new ArgumentOutOfRangeException("index"); + + public static void InvalidOperationException(string message) => throw new InvalidOperationException(message); + public static void ThrowArgumentNullException(ExceptionArgument argument) { - throw new ArgumentNullException(GetArgumentName(argument)); + throw new ArgumentNullException(argument.ToString()); } public static void ThrowArgumentException(ExceptionArgument argument, ExceptionReason reason) { - throw new ArgumentException(GetArgumentName(argument), GetExceptionReason(reason)); + throw new ArgumentException(argument.ToString(), GetExceptionReason(reason)); } public static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionReason reason) { - throw new ArgumentOutOfRangeException(GetArgumentName(argument), GetExceptionReason(reason)); + throw new ArgumentOutOfRangeException(argument.ToString(), GetExceptionReason(reason)); } public static void ThrowIndexOutOfRangeException() @@ -30,25 +50,6 @@ public static void ThrowIndexOutOfRangeException() throw new IndexOutOfRangeException(); } - private static string GetArgumentName(ExceptionArgument argument) - { - switch (argument) - { - case ExceptionArgument.key: - case ExceptionArgument.input: - case ExceptionArgument.value: - case ExceptionArgument.length: - case ExceptionArgument.text: - return argument.ToString(); - - case ExceptionArgument.offsetLength: - return "offset and length"; - - default: - Debug.Assert(false, "The enum value is not defined, please check the ExceptionArgument Enum."); - return ""; - } - } private static string GetExceptionReason(ExceptionReason reason) { switch (reason) diff --git a/src/Markdig/Markdig.targets b/src/Markdig/Markdig.targets index 4b8472f92..d77f45a11 100644 --- a/src/Markdig/Markdig.targets +++ b/src/Markdig/Markdig.targets @@ -6,7 +6,7 @@ en-US 0.18.3 Alexandre Mutel - net35;net40;netstandard2.0;netcoreapp2.1 + netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.1 Markdown CommonMark md html md2html https://github.com/lunet-io/markdig/blob/master/changelog.md BSD-2-Clause @@ -20,14 +20,21 @@ snupkg + + + + + + $(DefineConstants);NETCORE + + $(DefineConstants);NETCORE - - - - + + $(DefineConstants);NETCORE + $(NoWarn);CS1591 @@ -37,4 +44,5 @@ + diff --git a/src/Markdig/Markdown.cs b/src/Markdig/Markdown.cs index e6f2660fd..71fa084a4 100644 --- a/src/Markdig/Markdown.cs +++ b/src/Markdig/Markdown.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reflection; using Markdig.Extensions.SelfPipeline; +using Markdig.Helpers; using Markdig.Parsers; using Markdig.Renderers; using Markdig.Renderers.Normalize; @@ -68,8 +69,8 @@ public static MarkdownDocument Normalize(string markdown, TextWriter writer, Nor /// if markdown variable is null public static string ToHtml(string markdown, MarkdownPipeline pipeline = null) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); - pipeline = pipeline ?? new MarkdownPipelineBuilder().Build(); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); + pipeline ??= new MarkdownPipelineBuilder().Build(); pipeline = CheckForSelfPipeline(pipeline, markdown); var renderer = pipeline.GetCacheableHtmlRenderer(); @@ -94,9 +95,9 @@ public static string ToHtml(string markdown, MarkdownPipeline pipeline = null) /// if reader or writer variable are null public static MarkdownDocument ToHtml(string markdown, TextWriter writer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); - pipeline = pipeline ?? new MarkdownPipelineBuilder().Build(); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); + if (writer == null) ThrowHelper.ArgumentNullException_writer(); + pipeline ??= new MarkdownPipelineBuilder().Build(); pipeline = CheckForSelfPipeline(pipeline, markdown); // We override the renderer with our own writer @@ -120,9 +121,9 @@ public static MarkdownDocument ToHtml(string markdown, TextWriter writer, Markdo /// if markdown or writer variable are null public static object Convert(string markdown, IMarkdownRenderer renderer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); - if (renderer == null) throw new ArgumentNullException(nameof(renderer)); - pipeline = pipeline ?? new MarkdownPipelineBuilder().Build(); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); + if (renderer == null) ThrowHelper.ArgumentNullException(nameof(renderer)); + pipeline ??= new MarkdownPipelineBuilder().Build(); pipeline = CheckForSelfPipeline(pipeline, markdown); var document = Parse(markdown, pipeline, context); @@ -138,7 +139,7 @@ public static object Convert(string markdown, IMarkdownRenderer renderer, Markdo /// if markdown variable is null public static MarkdownDocument Parse(string markdown) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); return Parse(markdown, null); } @@ -152,8 +153,8 @@ public static MarkdownDocument Parse(string markdown) /// if markdown variable is null public static MarkdownDocument Parse(string markdown, MarkdownPipeline pipeline, MarkdownParserContext context = null) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); - pipeline = pipeline ?? new MarkdownPipelineBuilder().Build(); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); + pipeline ??= new MarkdownPipelineBuilder().Build(); pipeline = CheckForSelfPipeline(pipeline, markdown); return MarkdownParser.Parse(markdown, pipeline, context); @@ -180,9 +181,9 @@ private static MarkdownPipeline CheckForSelfPipeline(MarkdownPipeline pipeline, /// if reader or writer variable are null public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); - pipeline = pipeline ?? new MarkdownPipelineBuilder().Build(); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); + if (writer == null) ThrowHelper.ArgumentNullException_writer(); + pipeline ??= new MarkdownPipelineBuilder().Build(); pipeline = CheckForSelfPipeline(pipeline, markdown); // We override the renderer with our own writer @@ -211,7 +212,7 @@ public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, M /// if markdown variable is null public static string ToPlainText(string markdown, MarkdownPipeline pipeline = null, MarkdownParserContext context = null) { - if (markdown == null) throw new ArgumentNullException(nameof(markdown)); + if (markdown == null) ThrowHelper.ArgumentNullException_markdown(); var writer = new StringWriter(); ToPlainText(markdown, writer, pipeline, context); return writer.ToString(); diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index f038bdf02..831008fe0 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -34,7 +34,8 @@ using Markdig.Parsers; using Markdig.Parsers.Inlines; using Markdig.Extensions.Globalization; - +using Markdig.Helpers; + namespace Markdig { /// @@ -138,7 +139,7 @@ public static MarkdownPipelineBuilder UseSelfPipeline(this MarkdownPipelineBuild { if (pipeline.Extensions.Count != 0) { - throw new InvalidOperationException("The SelfPipeline extension cannot be used with other extensions"); + ThrowHelper.InvalidOperationException("The SelfPipeline extension cannot be used with other extensions"); } pipeline.Extensions.Add(new SelfPipelineExtension(defaultTag, defaultExtensions)); diff --git a/src/Markdig/MarkdownParserContext.cs b/src/Markdig/MarkdownParserContext.cs index aa326ee17..c8d50d842 100644 --- a/src/Markdig/MarkdownParserContext.cs +++ b/src/Markdig/MarkdownParserContext.cs @@ -5,7 +5,7 @@ namespace Markdig /// /// Provides a context that can be used as part of parsing Markdown documents. /// - public sealed class MarkdownParserContext + public class MarkdownParserContext { /// /// Gets or sets the context property collection. diff --git a/src/Markdig/MarkdownPipeline.cs b/src/Markdig/MarkdownPipeline.cs index 757d0ff4d..2ef66bc6f 100644 --- a/src/Markdig/MarkdownPipeline.cs +++ b/src/Markdig/MarkdownPipeline.cs @@ -22,8 +22,8 @@ public class MarkdownPipeline /// internal MarkdownPipeline(OrderedList extensions, BlockParserList blockParsers, InlineParserList inlineParsers, StringBuilderCache cache, TextWriter debugLog, ProcessDocumentDelegate documentProcessed) { - if (blockParsers == null) throw new ArgumentNullException(nameof(blockParsers)); - if (inlineParsers == null) throw new ArgumentNullException(nameof(inlineParsers)); + if (blockParsers == null) ThrowHelper.ArgumentNullException(nameof(blockParsers)); + if (inlineParsers == null) ThrowHelper.ArgumentNullException(nameof(inlineParsers)); // Add all default parsers Extensions = extensions; BlockParsers = blockParsers; @@ -57,7 +57,7 @@ internal MarkdownPipeline(OrderedList extensions, BlockParse /// The markdown renderer to setup public void Setup(IMarkdownRenderer renderer) { - if (renderer == null) throw new ArgumentNullException(nameof(renderer)); + if (renderer == null) ThrowHelper.ArgumentNullException(nameof(renderer)); foreach (var extension in Extensions) { extension.Setup(this, renderer); diff --git a/src/Markdig/MarkdownPipelineBuilder.cs b/src/Markdig/MarkdownPipelineBuilder.cs index 8bfb596be..5574342b5 100644 --- a/src/Markdig/MarkdownPipelineBuilder.cs +++ b/src/Markdig/MarkdownPipelineBuilder.cs @@ -111,7 +111,7 @@ public MarkdownPipeline Build() { if (extension == null) { - throw new InvalidOperationException("An extension cannot be null"); + ThrowHelper.InvalidOperationException("An extension cannot be null"); } extension.Setup(this); } diff --git a/src/Markdig/Parsers/BlockParser.cs b/src/Markdig/Parsers/BlockParser.cs index 010a28824..9aba31523 100644 --- a/src/Markdig/Parsers/BlockParser.cs +++ b/src/Markdig/Parsers/BlockParser.cs @@ -25,9 +25,9 @@ public bool HasOpeningCharacter(char c) { if (OpeningCharacters != null) { - for (int i = 0; i < OpeningCharacters.Length; i++) + foreach (char openingChar in OpeningCharacters) { - if (OpeningCharacters[i] == c) + if (openingChar == c) { return true; } diff --git a/src/Markdig/Parsers/BlockProcessor.cs b/src/Markdig/Parsers/BlockProcessor.cs index 02e74713b..a22af1dde 100644 --- a/src/Markdig/Parsers/BlockProcessor.cs +++ b/src/Markdig/Parsers/BlockProcessor.cs @@ -43,9 +43,9 @@ private BlockProcessor(BlockProcessor root) /// public BlockProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, BlockParserList parsers, MarkdownParserContext context) { - if (stringBuilders == null) throw new ArgumentNullException(nameof(stringBuilders)); - if (document == null) throw new ArgumentNullException(nameof(document)); - if (parsers == null) throw new ArgumentNullException(nameof(parsers)); + if (stringBuilders == null) ThrowHelper.ArgumentNullException(nameof(stringBuilders)); + if (document == null) ThrowHelper.ArgumentNullException(nameof(document)); + if (parsers == null) ThrowHelper.ArgumentNullException(nameof(parsers)); parserStateCache = new BlockParserStateCache(this); StringBuilders = stringBuilders; Document = document; @@ -400,8 +400,8 @@ public void GoToCodeIndent(int columnOffset = 0) /// The block must be opened public void Open(Block block) { - if (block == null) throw new ArgumentNullException(nameof(block)); - if (!block.IsOpen) throw new ArgumentException("The block must be opened", nameof(block)); + if (block == null) ThrowHelper.ArgumentNullException(nameof(block)); + if (!block.IsOpen) ThrowHelper.ArgumentException("The block must be opened", nameof(block)); OpenedBlocks.Add(block); } @@ -477,7 +477,7 @@ public void ReleaseChild() { if (this == root) { - throw new InvalidOperationException("Cannot release the root parser state"); + ThrowHelper.InvalidOperationException("Cannot release the root parser state"); } parserStateCache.Release(this); } @@ -633,7 +633,7 @@ private void TryContinueBlocks() // If a parser is adding a block, it must be the last of the list if ((i + 1) < OpenedBlocks.Count && NewBlocks.Count > 0) { - throw new InvalidOperationException("A pending parser cannot add a new block when it is not the last pending block"); + ThrowHelper.InvalidOperationException("A pending parser cannot add a new block when it is not the last pending block"); } // If we have a leaf block @@ -680,7 +680,7 @@ private void TryOpenBlocks() // Security check so that the parser can't go into a crazy infinite loop if one extension is messing if (previousStart == Start) { - throw new InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse blocks at line [{LineIndex}] with line [{Line}]"); + ThrowHelper.InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse blocks at line [{LineIndex}] with line [{Line}]"); } previousStart = Start; @@ -810,7 +810,7 @@ private void ProcessNewBlocks(BlockState result, bool allowClosing) if (block.Parser == null) { - throw new InvalidOperationException($"The new block [{block.GetType()}] must have a valid Parser property"); + ThrowHelper.InvalidOperationException($"The new block [{block.GetType()}] must have a valid Parser property"); } block.Line = LineIndex; @@ -826,7 +826,7 @@ private void ProcessNewBlocks(BlockState result, bool allowClosing) if (newBlocks.Count > 0) { - throw new InvalidOperationException( + ThrowHelper.InvalidOperationException( "The NewBlocks is not empty. This is happening if a LeafBlock is not the last to be pushed"); } } diff --git a/src/Markdig/Parsers/BlockStateExtensions.cs b/src/Markdig/Parsers/BlockStateExtensions.cs index ee0e796ea..4e99d7829 100644 --- a/src/Markdig/Parsers/BlockStateExtensions.cs +++ b/src/Markdig/Parsers/BlockStateExtensions.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. using System.Runtime.CompilerServices; @@ -16,7 +16,7 @@ public static class BlockStateExtensions /// /// State of the block. /// true if the block state is in discard state - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsDiscard(this BlockState blockState) { return blockState == BlockState.ContinueDiscard || blockState == BlockState.BreakDiscard; @@ -27,7 +27,7 @@ public static bool IsDiscard(this BlockState blockState) /// /// State of the block. /// true if the block state is in continue state - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsContinue(this BlockState blockState) { return blockState == BlockState.Continue || blockState == BlockState.ContinueDiscard; @@ -38,7 +38,7 @@ public static bool IsContinue(this BlockState blockState) /// /// State of the block. /// true if the block state is in break state - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBreak(this BlockState blockState) { return blockState == BlockState.Break || blockState == BlockState.BreakDiscard; diff --git a/src/Markdig/Parsers/InlineProcessor.cs b/src/Markdig/Parsers/InlineProcessor.cs index 0eed5035c..ac3405bb1 100644 --- a/src/Markdig/Parsers/InlineProcessor.cs +++ b/src/Markdig/Parsers/InlineProcessor.cs @@ -38,9 +38,9 @@ public class InlineProcessor /// public InlineProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, InlineParserList parsers, bool preciseSourcelocation, MarkdownParserContext context) { - if (stringBuilders == null) throw new ArgumentNullException(nameof(stringBuilders)); - if (document == null) throw new ArgumentNullException(nameof(document)); - if (parsers == null) throw new ArgumentNullException(nameof(parsers)); + if (stringBuilders == null) ThrowHelper.ArgumentNullException(nameof(stringBuilders)); + if (document == null) ThrowHelper.ArgumentNullException(nameof(document)); + if (parsers == null) ThrowHelper.ArgumentNullException(nameof(parsers)); StringBuilders = stringBuilders; Document = document; Parsers = parsers; @@ -176,7 +176,7 @@ public int GetSourcePosition(int sliceOffset, out int lineIndex, out int column) /// The leaf block. public void ProcessInlineLeaf(LeafBlock leafBlock) { - if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock)); + if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock(); // clear parser states Array.Clear(ParserStates, 0, ParserStates.Length); @@ -201,7 +201,7 @@ public void ProcessInlineLeaf(LeafBlock leafBlock) // Security check so that the parser can't go into a crazy infinite loop if one extension is messing if (previousStart == text.Start) { - throw new InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse inlines for block [{leafBlock.GetType().Name}] at position ({leafBlock.ToPositionText()}"); + ThrowHelper.InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse inlines for block [{leafBlock.GetType().Name}] at position ({leafBlock.ToPositionText()}"); } previousStart = text.Start; diff --git a/src/Markdig/Parsers/Inlines/EmphasisDescriptor.cs b/src/Markdig/Parsers/Inlines/EmphasisDescriptor.cs index 3b2f6dd2b..9cd4011f6 100644 --- a/src/Markdig/Parsers/Inlines/EmphasisDescriptor.cs +++ b/src/Markdig/Parsers/Inlines/EmphasisDescriptor.cs @@ -2,6 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using Markdig.Helpers; using System; namespace Markdig.Parsers.Inlines @@ -20,9 +21,9 @@ public class EmphasisDescriptor /// if set to true the emphasis can be used inside a word. public EmphasisDescriptor(char character, int minimumCount, int maximumCount, bool enableWithinWord) { - if (minimumCount < 1) throw new ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be >= 1"); - if (maximumCount < 1) throw new ArgumentOutOfRangeException(nameof(maximumCount), "maximumCount must be >= 1"); - if (minimumCount > maximumCount) throw new ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be <= maximumCount"); + if (minimumCount < 1) ThrowHelper.ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be >= 1"); + if (maximumCount < 1) ThrowHelper.ArgumentOutOfRangeException(nameof(maximumCount), "maximumCount must be >= 1"); + if (minimumCount > maximumCount) ThrowHelper.ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be <= maximumCount"); Character = character; MinimumCount = minimumCount; diff --git a/src/Markdig/Parsers/Inlines/EmphasisInlineParser.cs b/src/Markdig/Parsers/Inlines/EmphasisInlineParser.cs index aef22033a..98a2c1e0f 100644 --- a/src/Markdig/Parsers/Inlines/EmphasisInlineParser.cs +++ b/src/Markdig/Parsers/Inlines/EmphasisInlineParser.cs @@ -77,7 +77,7 @@ public override void Initialize() var emphasis = EmphasisDescriptors[i]; if (Array.IndexOf(OpeningCharacters, emphasis.Character) >= 0) { - throw new InvalidOperationException( + ThrowHelper.InvalidOperationException( $"The character `{emphasis.Character}` is already used by another emphasis descriptor"); } diff --git a/src/Markdig/Parsers/Inlines/HtmlEntityParser.cs b/src/Markdig/Parsers/Inlines/HtmlEntityParser.cs index c1fe9c763..3b423664c 100644 --- a/src/Markdig/Parsers/Inlines/HtmlEntityParser.cs +++ b/src/Markdig/Parsers/Inlines/HtmlEntityParser.cs @@ -2,7 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.Text; +using System; using Markdig.Helpers; using Markdig.Syntax; using Markdig.Syntax.Inlines; @@ -27,10 +27,7 @@ public HtmlEntityParser() public static bool TryParse(ref StringSlice slice, out string literal, out int match) { literal = null; - int entityNameStart; - int entityNameLength; - int entityValue; - match = HtmlHelper.ScanEntity(slice, out entityValue, out entityNameStart, out entityNameLength); + match = HtmlHelper.ScanEntity(slice, out int entityValue, out int entityNameStart, out int entityNameLength); if (match == 0) { return false; @@ -38,11 +35,11 @@ public static bool TryParse(ref StringSlice slice, out string literal, out int m if (entityNameLength > 0) { - literal = EntityHelper.DecodeEntity(new StringSlice(slice.Text, entityNameStart, entityNameStart + entityNameLength - 1).ToString()); + literal = EntityHelper.DecodeEntity(slice.Text.AsSpan(entityNameStart, entityNameLength)); } else if (entityValue >= 0) { - literal = (entityValue == 0 ? null : EntityHelper.DecodeEntity(entityValue)) ?? CharHelper.ZeroSafeString; + literal = EntityHelper.DecodeEntity(entityValue); } return literal != null; } diff --git a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs index fdcae31d0..e4dc405cb 100644 --- a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs +++ b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs @@ -300,19 +300,19 @@ private bool TryProcessLinkOrImage(InlineProcessor inlineState, ref StringSlice private void MarkParentAsInactive(Inline inline) { - if (inline == null) + while (inline != null) { - return; - } - - foreach (var parent in inline.FindParentOfType()) - { - if (parent.IsImage) + if (inline is LinkDelimiterInline linkInline) { - break; + if (linkInline.IsImage) + { + break; + } + + linkInline.IsActive = false; } - parent.IsActive = false; + inline = inline.Parent; } } } diff --git a/src/Markdig/Parsers/ListBlockParser.cs b/src/Markdig/Parsers/ListBlockParser.cs index 240eb5433..61ebf84e9 100644 --- a/src/Markdig/Parsers/ListBlockParser.cs +++ b/src/Markdig/Parsers/ListBlockParser.cs @@ -41,14 +41,14 @@ public override void Initialize() { if (itemParser.OpeningCharacters == null) { - throw new InvalidOperationException($"The list item parser of type [{itemParser.GetType()}] cannot have OpeningCharacters to null. It must define a list of valid opening characters"); + ThrowHelper.InvalidOperationException($"The list item parser of type [{itemParser.GetType()}] cannot have OpeningCharacters to null. It must define a list of valid opening characters"); } foreach (var openingCharacter in itemParser.OpeningCharacters) { if (tempMap.ContainsKey(openingCharacter)) { - throw new InvalidOperationException( + ThrowHelper.InvalidOperationException( $"A list item parser with the same opening character `{openingCharacter}` is already registered"); } tempMap.Add(openingCharacter, itemParser); diff --git a/src/Markdig/Parsers/MarkdownParser.cs b/src/Markdig/Parsers/MarkdownParser.cs index 6bffeb238..011e98d92 100644 --- a/src/Markdig/Parsers/MarkdownParser.cs +++ b/src/Markdig/Parsers/MarkdownParser.cs @@ -42,8 +42,8 @@ public sealed class MarkdownParser /// private MarkdownParser(string text, MarkdownPipeline pipeline, MarkdownParserContext context) { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (pipeline == null) throw new ArgumentNullException(nameof(pipeline)); + if (text == null) ThrowHelper.ArgumentNullException_text(); + if (pipeline == null) ThrowHelper.ArgumentNullException(nameof(pipeline)); roughLineCountEstimate = text.Length / 40; text = FixupZero(text); @@ -77,8 +77,8 @@ private MarkdownParser(string text, MarkdownPipeline pipeline, MarkdownParserCon /// if reader variable is null public static MarkdownDocument Parse(string text, MarkdownPipeline pipeline = null, MarkdownParserContext context = null) { - if (text == null) throw new ArgumentNullException(nameof(text)); - pipeline = pipeline ?? new MarkdownPipelineBuilder().Build(); + if (text == null) ThrowHelper.ArgumentNullException_text(); + pipeline ??= new MarkdownPipelineBuilder().Build(); // Perform the parsing var markdownParser = new MarkdownParser(text, pipeline, context); @@ -131,7 +131,7 @@ private void ProcessBlocks() /// The text to secure. private string FixupZero(string text) { - return text.Replace('\0', CharHelper.ZeroSafeChar); + return text.Replace('\0', CharHelper.ReplacementChar); } private sealed class ContainerItemCache : DefaultObjectCache diff --git a/src/Markdig/Parsers/OrderedListItemParser.cs b/src/Markdig/Parsers/OrderedListItemParser.cs index 20c57d1a8..cca05de6d 100644 --- a/src/Markdig/Parsers/OrderedListItemParser.cs +++ b/src/Markdig/Parsers/OrderedListItemParser.cs @@ -33,9 +33,9 @@ protected bool TryParseDelimiter(BlockProcessor state, out char orderedDelimiter { // Check if we have an ordered delimiter orderedDelimiter = state.CurrentChar; - for (int i = 0; i < OrderedDelimiters.Length; i++) + foreach (char delimiter in OrderedDelimiters) { - if (OrderedDelimiters[i] == orderedDelimiter) + if (delimiter == orderedDelimiter) { state.NextChar(); return true; diff --git a/src/Markdig/Parsers/ParserList.cs b/src/Markdig/Parsers/ParserList.cs index fcbbfa0fa..e63b5f392 100644 --- a/src/Markdig/Parsers/ParserList.cs +++ b/src/Markdig/Parsers/ParserList.cs @@ -29,7 +29,7 @@ protected ParserList(IEnumerable parsersArg) : base(parsersArg) var parser = this[i]; if (parser == null) { - throw new InvalidOperationException("Unexpected null parser found"); + ThrowHelper.InvalidOperationException("Unexpected null parser found"); } parser.Initialize(); @@ -100,8 +100,8 @@ protected ParserList(IEnumerable parsersArg) : base(parsersArg) /// /// The opening character. /// A list of parsers valid for the specified opening character or null if no parsers registered. - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] - public T[] GetParsersForOpeningCharacter(char openingChar) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] GetParsersForOpeningCharacter(uint openingChar) { return charMap[openingChar]; } @@ -113,7 +113,7 @@ public T[] GetParsersForOpeningCharacter(char openingChar) /// The start. /// The end. /// Index position within the string of the first opening character found in the specified text; if not found, returns -1 - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOfOpeningCharacter(string text, int start, int end) { return charMap.IndexOfOpeningCharacter(text, start, end); diff --git a/src/Markdig/Renderers/Html/HtmlAttributes.cs b/src/Markdig/Renderers/Html/HtmlAttributes.cs index c9765d0f8..9613a60d7 100644 --- a/src/Markdig/Renderers/Html/HtmlAttributes.cs +++ b/src/Markdig/Renderers/Html/HtmlAttributes.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using Markdig.Helpers; using Markdig.Syntax; namespace Markdig.Renderers.Html @@ -41,7 +42,7 @@ public HtmlAttributes() /// The css class name. public void AddClass(string name) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) ThrowHelper.ArgumentNullException_name(); if (Classes == null) { Classes = new List(2); @@ -61,7 +62,7 @@ public void AddClass(string name) /// The value. public void AddProperty(string name, string value) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) ThrowHelper.ArgumentNullException_name(); if (Properties == null) { Properties = new List>(2); // Use half list compare to default capacity (4), as we don't expect lots of classes @@ -76,7 +77,7 @@ public void AddProperty(string name, string value) /// The value. public void AddPropertyIfNotExist(string name, object value) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) ThrowHelper.ArgumentNullException_name(); if (Properties == null) { Properties = new List>(4); @@ -104,7 +105,7 @@ public void AddPropertyIfNotExist(string name, object value) /// public void CopyTo(HtmlAttributes htmlAttributes, bool mergeIdAndProperties = false, bool shared = true) { - if (htmlAttributes == null) throw new ArgumentNullException(nameof(htmlAttributes)); + if (htmlAttributes == null) ThrowHelper.ArgumentNullException(nameof(htmlAttributes)); // Add html htmlAttributes to the object if (!mergeIdAndProperties || Id != null) { diff --git a/src/Markdig/Renderers/HtmlRenderer.cs b/src/Markdig/Renderers/HtmlRenderer.cs index 22f1ae6e6..befe55013 100644 --- a/src/Markdig/Renderers/HtmlRenderer.cs +++ b/src/Markdig/Renderers/HtmlRenderer.cs @@ -91,7 +91,7 @@ public HtmlRenderer(TextWriter writer) : base(writer) /// /// The content. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public HtmlRenderer WriteEscape(string content) { if (string.IsNullOrEmpty(content)) @@ -107,7 +107,7 @@ public HtmlRenderer WriteEscape(string content) /// The slice. /// Only escape < and & /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public HtmlRenderer WriteEscape(ref StringSlice slice, bool softEscape = false) { if (slice.Start > slice.End) @@ -123,7 +123,7 @@ public HtmlRenderer WriteEscape(ref StringSlice slice, bool softEscape = false) /// The slice. /// Only escape < and & /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public HtmlRenderer WriteEscape(StringSlice slice, bool softEscape = false) { return WriteEscape(ref slice, softEscape); @@ -318,12 +318,12 @@ public HtmlRenderer WriteEscapeUrl(string content) /// /// Writes the attached on the specified . /// - /// The object. + /// The object. /// - public HtmlRenderer WriteAttributes(MarkdownObject obj) + public HtmlRenderer WriteAttributes(MarkdownObject markdownObject) { - if (obj == null) throw new ArgumentNullException(nameof(obj)); - return WriteAttributes(obj.TryGetAttributes()); + if (markdownObject == null) ThrowHelper.ArgumentNullException_markdownObject(); + return WriteAttributes(markdownObject.TryGetAttributes()); } /// @@ -383,7 +383,7 @@ public HtmlRenderer WriteAttributes(HtmlAttributes attributes, FuncThis instance public HtmlRenderer WriteLeafRawLines(LeafBlock leafBlock, bool writeEndOfLines, bool escape, bool softEscape = false) { - if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock)); + if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock(); if (leafBlock.Lines.Lines != null) { var lines = leafBlock.Lines; diff --git a/src/Markdig/Renderers/Normalize/NormalizeRenderer.cs b/src/Markdig/Renderers/Normalize/NormalizeRenderer.cs index dcb364362..d8e460b00 100644 --- a/src/Markdig/Renderers/Normalize/NormalizeRenderer.cs +++ b/src/Markdig/Renderers/Normalize/NormalizeRenderer.cs @@ -6,6 +6,7 @@ using System.IO; using Markdig.Syntax; using Markdig.Renderers.Normalize.Inlines; +using Markdig.Helpers; namespace Markdig.Renderers.Normalize { @@ -130,7 +131,7 @@ public void FinishBlock(bool emptyLine) /// This instance public NormalizeRenderer WriteLeafRawLines(LeafBlock leafBlock, bool writeEndOfLines, bool indent = false) { - if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock)); + if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock(); if (leafBlock.Lines.Lines != null) { var lines = leafBlock.Lines; diff --git a/src/Markdig/Renderers/TextRendererBase.cs b/src/Markdig/Renderers/TextRendererBase.cs index c4eb973b8..42d609f1a 100644 --- a/src/Markdig/Renderers/TextRendererBase.cs +++ b/src/Markdig/Renderers/TextRendererBase.cs @@ -26,7 +26,7 @@ public abstract class TextRendererBase : RendererBase /// protected TextRendererBase(TextWriter writer) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + if (writer == null) ThrowHelper.ArgumentNullException_writer(); this.Writer = writer; // By default we output a newline with '\n' only even on Windows platforms Writer.NewLine = "\n"; @@ -43,7 +43,7 @@ public TextWriter Writer { if (value == null) { - throw new ArgumentNullException(nameof(value)); + ThrowHelper.ArgumentNullException(nameof(value)); } writer = value; @@ -97,7 +97,7 @@ internal void Reset() } else { - throw new InvalidOperationException("Cannot reset this TextWriter instance"); + ThrowHelper.InvalidOperationException("Cannot reset this TextWriter instance"); } previousWasLine = true; @@ -119,7 +119,7 @@ public T EnsureLine() public void PushIndent(string indent) { - if (indent == null) throw new ArgumentNullException(nameof(indent)); + if (indent == null) ThrowHelper.ArgumentNullException(nameof(indent)); indents.Add(indent); } @@ -147,7 +147,7 @@ private void WriteIndent() /// /// The content. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Write(string content) { WriteIndent(); @@ -161,7 +161,7 @@ public T Write(string content) /// /// The slice. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Write(ref StringSlice slice) { if (slice.Start > slice.End) @@ -176,7 +176,7 @@ public T Write(ref StringSlice slice) /// /// The slice. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Write(StringSlice slice) { return Write(ref slice); @@ -187,7 +187,7 @@ public T Write(StringSlice slice) /// /// The content. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Write(char content) { WriteIndent(); @@ -241,7 +241,7 @@ public T Write(string content, int offset, int length) /// Writes a newline. /// /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T WriteLine() { WriteIndent(); @@ -255,7 +255,7 @@ public T WriteLine() /// /// The content. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T WriteLine(string content) { WriteIndent(); @@ -269,10 +269,10 @@ public T WriteLine(string content) /// /// The leaf block. /// This instance - [MethodImpl(MethodImplOptionPortable.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T WriteLeafInline(LeafBlock leafBlock) { - if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock)); + if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock(); var inline = (Inline) leafBlock.Inline; if (inline != null) { diff --git a/src/Markdig/Syntax/ContainerBlock.cs b/src/Markdig/Syntax/ContainerBlock.cs index a0c8b42b7..3ee68f2ad 100644 --- a/src/Markdig/Syntax/ContainerBlock.cs +++ b/src/Markdig/Syntax/ContainerBlock.cs @@ -27,7 +27,7 @@ public abstract class ContainerBlock : Block, IList /// The parser used to create this block. protected ContainerBlock(BlockParser parser) : base(parser) { - children = ArrayHelper.Empty; + children = Array.Empty(); } /// @@ -56,10 +56,12 @@ IEnumerator IEnumerable.GetEnumerator() public void Add(Block item) { - if (item == null) throw new ArgumentNullException(nameof(item)); + if (item == null) + ThrowHelper.ArgumentNullException_item(); + if (item.Parent != null) { - throw new ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)"); + ThrowHelper.ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)"); } if (Count == children.Length) @@ -104,7 +106,9 @@ public void Clear() public bool Contains(Block item) { - if (item == null) throw new ArgumentNullException(nameof(item)); + if (item == null) + ThrowHelper.ArgumentNullException_item(); + for (int i = 0; i < Count; i++) { if (children[i] == item) @@ -122,7 +126,9 @@ public void CopyTo(Block[] array, int arrayIndex) public bool Remove(Block item) { - if (item == null) throw new ArgumentNullException(nameof(item)); + if (item == null) + ThrowHelper.ArgumentNullException_item(); + for (int i = Count - 1; i >= 0; i--) { if (children[i] == item) @@ -140,7 +146,9 @@ public bool Remove(Block item) public int IndexOf(Block item) { - if (item == null) throw new ArgumentNullException(nameof(item)); + if (item == null) + ThrowHelper.ArgumentNullException_item(); + for (int i = 0; i < Count; i++) { if (children[i] == item) @@ -153,14 +161,16 @@ public int IndexOf(Block item) public void Insert(int index, Block item) { - if (item == null) throw new ArgumentNullException(nameof(item)); + if (item == null) + ThrowHelper.ArgumentNullException_item(); + if (item.Parent != null) { - throw new ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)"); + ThrowHelper.ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)"); } - if (index < 0 || index > Count) + if ((uint)index > (uint)Count) { - throw new ArgumentOutOfRangeException(nameof(index)); + ThrowHelper.ArgumentOutOfRangeException_index(); } if (Count == children.Length) { @@ -177,7 +187,9 @@ public void Insert(int index, Block item) public void RemoveAt(int index) { - if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); + if ((uint)index > (uint)Count) + ThrowHelper.ArgumentOutOfRangeException_index(); + Count--; // previous children var item = children[index]; @@ -210,13 +222,13 @@ public Block this[int index] public void Sort(IComparer comparer) { - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + if (comparer == null) ThrowHelper.ArgumentNullException(nameof(comparer)); Array.Sort(children, 0, Count, comparer); } public void Sort(Comparison comparison) { - if (comparison == null) throw new ArgumentNullException(nameof(comparison)); + if (comparison == null) ThrowHelper.ArgumentNullException(nameof(comparison)); Array.Sort(children, 0, Count, new BlockComparer(comparison)); } diff --git a/src/Markdig/Syntax/Inlines/ContainerInline.cs b/src/Markdig/Syntax/Inlines/ContainerInline.cs index 676d057c7..fe27e1acf 100644 --- a/src/Markdig/Syntax/Inlines/ContainerInline.cs +++ b/src/Markdig/Syntax/Inlines/ContainerInline.cs @@ -1,6 +1,7 @@ // 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 Markdig.Helpers; using System; using System.Collections; @@ -50,10 +51,10 @@ public void Clear() /// Inline has already a parent public virtual ContainerInline AppendChild(Inline child) { - if (child == null) throw new ArgumentNullException(nameof(child)); + if (child == null) ThrowHelper.ArgumentNullException(nameof(child)); if (child.Parent != null) { - throw new ArgumentException("Inline has already a parent", nameof(child)); + ThrowHelper.ArgumentException("Inline has already a parent", nameof(child)); } if (FirstChild == null) @@ -98,7 +99,7 @@ public IEnumerable FindDescendants() where T : Inline { if (FirstChild is null) { - return ArrayHelper.Empty; + return Array.Empty(); } else { @@ -145,7 +146,7 @@ internal IEnumerable FindDescendantsInternal() where T : MarkdownObject /// The parent. public void MoveChildrenAfter(Inline parent) { - if (parent == null) throw new ArgumentNullException(nameof(parent)); + if (parent == null) ThrowHelper.ArgumentNullException(nameof(parent)); var child = FirstChild; var nextSibling = parent; while (child != null) @@ -166,7 +167,7 @@ public void MoveChildrenAfter(Inline parent) /// If the container is null public void EmbraceChildrenBy(ContainerInline container) { - if (container == null) throw new ArgumentNullException(nameof(container)); + if (container == null) ThrowHelper.ArgumentNullException(nameof(container)); var child = FirstChild; while (child != null) { @@ -238,7 +239,7 @@ public struct Enumerator : IEnumerator public Enumerator(ContainerInline container) : this() { - if (container == null) throw new ArgumentNullException(nameof(container)); + if (container == null) ThrowHelper.ArgumentNullException(nameof(container)); this.container = container; currentChild = nextChild = container.FirstChild; } diff --git a/src/Markdig/Syntax/Inlines/DelimiterInline.cs b/src/Markdig/Syntax/Inlines/DelimiterInline.cs index 1e292414f..da59bb916 100644 --- a/src/Markdig/Syntax/Inlines/DelimiterInline.cs +++ b/src/Markdig/Syntax/Inlines/DelimiterInline.cs @@ -17,7 +17,7 @@ public abstract class DelimiterInline : ContainerInline { protected DelimiterInline(InlineParser parser) { - if (parser == null) throw new ArgumentNullException(nameof(parser)); + if (parser == null) ThrowHelper.ArgumentNullException(nameof(parser)); Parser = parser; IsActive = true; } diff --git a/src/Markdig/Syntax/Inlines/EmphasisDelimiterInline.cs b/src/Markdig/Syntax/Inlines/EmphasisDelimiterInline.cs index b1e908c67..d6e5ece6e 100644 --- a/src/Markdig/Syntax/Inlines/EmphasisDelimiterInline.cs +++ b/src/Markdig/Syntax/Inlines/EmphasisDelimiterInline.cs @@ -23,7 +23,10 @@ public class EmphasisDelimiterInline : DelimiterInline /// public EmphasisDelimiterInline(InlineParser parser, EmphasisDescriptor descriptor) : base(parser) { - Descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor)); + if (descriptor is null) + ThrowHelper.ArgumentNullException(nameof(descriptor)); + + Descriptor = descriptor; DelimiterChar = descriptor.Character; } diff --git a/src/Markdig/Syntax/Inlines/Inline.cs b/src/Markdig/Syntax/Inlines/Inline.cs index 99dff49a7..2b79fb337 100644 --- a/src/Markdig/Syntax/Inlines/Inline.cs +++ b/src/Markdig/Syntax/Inlines/Inline.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using Markdig.Helpers; using Markdig.Parsers; namespace Markdig.Syntax.Inlines @@ -42,10 +43,10 @@ public abstract class Inline : MarkdownObject, IInline /// Inline has already a parent public void InsertAfter(Inline next) { - if (next == null) throw new ArgumentNullException(nameof(next)); + if (next == null) ThrowHelper.ArgumentNullException(nameof(next)); if (next.Parent != null) { - throw new ArgumentException("Inline has already a parent", nameof(next)); + ThrowHelper.ArgumentException("Inline has already a parent", nameof(next)); } var previousNext = NextSibling; @@ -73,10 +74,10 @@ public void InsertAfter(Inline next) /// Inline has already a parent public void InsertBefore(Inline previous) { - if (previous == null) throw new ArgumentNullException(nameof(previous)); + if (previous == null) ThrowHelper.ArgumentNullException(nameof(previous)); if (previous.Parent != null) { - throw new ArgumentException("Inline has already a parent", nameof(previous)); + ThrowHelper.ArgumentException("Inline has already a parent", nameof(previous)); } var previousSibling = PreviousSibling; @@ -129,7 +130,7 @@ public void Remove() /// If inline is null public Inline ReplaceBy(Inline inline, bool copyChildren = true) { - if (inline == null) throw new ArgumentNullException(nameof(inline)); + if (inline == null) ThrowHelper.ArgumentNullException(nameof(inline)); // Save sibling var parent = Parent; @@ -271,7 +272,7 @@ protected virtual void OnChildInsert(Inline child) /// public void DumpTo(TextWriter writer) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + if (writer == null) ThrowHelper.ArgumentNullException_writer(); DumpTo(writer, 0); } @@ -283,7 +284,7 @@ public void DumpTo(TextWriter writer) /// if writer is null public void DumpTo(TextWriter writer, int level) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + if (writer == null) ThrowHelper.ArgumentNullException_writer(); for (int i = 0; i < level; i++) { writer.Write(' '); diff --git a/src/Markdig/Syntax/Inlines/LiteralInline.cs b/src/Markdig/Syntax/Inlines/LiteralInline.cs index 46c4be2a8..5f2171520 100644 --- a/src/Markdig/Syntax/Inlines/LiteralInline.cs +++ b/src/Markdig/Syntax/Inlines/LiteralInline.cs @@ -39,7 +39,7 @@ public LiteralInline(StringSlice content) /// public LiteralInline(string text) { - if (text == null) throw new ArgumentNullException(nameof(text)); + if (text == null) ThrowHelper.ArgumentNullException_text(); Content = new StringSlice(text); } diff --git a/src/Markdig/Syntax/LinkReferenceDefinitionExtensions.cs b/src/Markdig/Syntax/LinkReferenceDefinitionExtensions.cs index abfa3d513..597720672 100644 --- a/src/Markdig/Syntax/LinkReferenceDefinitionExtensions.cs +++ b/src/Markdig/Syntax/LinkReferenceDefinitionExtensions.cs @@ -1,6 +1,7 @@ -// 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 Markdig.Helpers; using System; using System.Collections.Generic; @@ -15,7 +16,7 @@ public static class LinkReferenceDefinitionExtensions public static bool ContainsLinkReferenceDefinition(this MarkdownDocument document, string label) { - if (label == null) throw new ArgumentNullException(nameof(label)); + if (label == null) ThrowHelper.ArgumentNullException_label(); var references = document.GetData(DocumentKey) as LinkReferenceDefinitionGroup; if (references == null) { @@ -26,14 +27,14 @@ public static bool ContainsLinkReferenceDefinition(this MarkdownDocument documen public static void SetLinkReferenceDefinition(this MarkdownDocument document, string label, LinkReferenceDefinition linkReferenceDefinition) { - if (label == null) throw new ArgumentNullException(nameof(label)); + if (label == null) ThrowHelper.ArgumentNullException_label(); var references = document.GetLinkReferenceDefinitions(); references.Set(label, linkReferenceDefinition); } public static bool TryGetLinkReferenceDefinition(this MarkdownDocument document, string label, out LinkReferenceDefinition linkReferenceDefinition) { - if (label == null) throw new ArgumentNullException(nameof(label)); + if (label == null) ThrowHelper.ArgumentNullException_label(); linkReferenceDefinition = null; var references = document.GetData(DocumentKey) as LinkReferenceDefinitionGroup; if (references == null) diff --git a/src/Markdig/Syntax/LinkReferenceDefinitionGroup.cs b/src/Markdig/Syntax/LinkReferenceDefinitionGroup.cs index e3e8e263c..42da166b4 100644 --- a/src/Markdig/Syntax/LinkReferenceDefinitionGroup.cs +++ b/src/Markdig/Syntax/LinkReferenceDefinitionGroup.cs @@ -2,6 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using Markdig.Helpers; using System; using System.Collections.Generic; @@ -28,7 +29,7 @@ public LinkReferenceDefinitionGroup() : base(null) public void Set(string label, LinkReferenceDefinition link) { - if (link == null) throw new ArgumentNullException(nameof(link)); + if (link == null) ThrowHelper.ArgumentNullException(nameof(link)); if (!Contains(link)) { Add(link); diff --git a/src/Markdig/Syntax/MarkdownObject.cs b/src/Markdig/Syntax/MarkdownObject.cs index 484bc404f..710ed4182 100644 --- a/src/Markdig/Syntax/MarkdownObject.cs +++ b/src/Markdig/Syntax/MarkdownObject.cs @@ -1,6 +1,7 @@ -// 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 Markdig.Helpers; using System; namespace Markdig.Syntax @@ -55,7 +56,7 @@ public string ToPositionText() /// if key is null public void SetData(object key, object value) { - if (key == null) throw new ArgumentNullException(nameof(key)); + if (key == null) ThrowHelper.ArgumentNullException_key(); if (attachedDatas == null) { attachedDatas = new DataEntry[1]; @@ -89,7 +90,7 @@ public void SetData(object key, object value) /// if key is null public bool ContainsData(object key) { - if (key == null) throw new ArgumentNullException(nameof(key)); + if (key == null) ThrowHelper.ArgumentNullException_key(); if (attachedDatas == null) { return false; @@ -113,7 +114,7 @@ public bool ContainsData(object key) /// if key is null public object GetData(object key) { - if (key == null) throw new ArgumentNullException(nameof(key)); + if (key == null) ThrowHelper.ArgumentNullException_key(); if (attachedDatas == null) { return null; @@ -136,7 +137,7 @@ public object GetData(object key) /// public bool RemoveData(object key) { - if (key == null) throw new ArgumentNullException(nameof(key)); + if (key == null) ThrowHelper.ArgumentNullException_key(); if (attachedDatas == null) { return true; diff --git a/src/Markdig/Syntax/MarkdownObjectExtensions.cs b/src/Markdig/Syntax/MarkdownObjectExtensions.cs index efb1e44c4..012bfb296 100644 --- a/src/Markdig/Syntax/MarkdownObjectExtensions.cs +++ b/src/Markdig/Syntax/MarkdownObjectExtensions.cs @@ -2,9 +2,9 @@ // 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.Collections.Generic; using System.Diagnostics; -using Markdig.Helpers; using Markdig.Syntax.Inlines; namespace Markdig.Syntax @@ -94,7 +94,7 @@ public static IEnumerable Descendants(this MarkdownObject markdownObject) } } - return ArrayHelper.Empty; + return Array.Empty(); } /// @@ -124,7 +124,7 @@ public static IEnumerable Descendants(this ContainerBlock block) where T : } else { - return ArrayHelper.Empty; + return Array.Empty(); } }