mirror of
https://github.com/xoofx/markdig.git
synced 2026-10-08 06:38:39 +00:00
Optimize CharacterMap further
This commit is contained in:
@@ -17,8 +17,8 @@ namespace Markdig.Helpers
|
||||
public class CharacterMap<T> where T : class
|
||||
{
|
||||
private readonly T[] asciiMap;
|
||||
private readonly Dictionary<char, T> nonAsciiMap;
|
||||
private readonly BitVector128 isOpeningCharacter;
|
||||
private readonly Dictionary<uint, T> nonAsciiMap;
|
||||
private readonly BoolVector128 isOpeningCharacter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CharacterMap{T}"/> class.
|
||||
@@ -34,49 +34,31 @@ namespace Markdig.Helpers
|
||||
foreach (var map in maps)
|
||||
{
|
||||
var openingChar = map.Key;
|
||||
|
||||
charSet.Add(openingChar);
|
||||
|
||||
if (openingChar < 128 && openingChar > maxChar)
|
||||
{
|
||||
maxChar = openingChar;
|
||||
}
|
||||
else if (openingChar >= 128 && nonAsciiMap == null)
|
||||
{
|
||||
// Initialize only if with have an actual non-ASCII opening character
|
||||
nonAsciiMap = new Dictionary<char, T>();
|
||||
}
|
||||
maxChar = Math.Max(maxChar, openingChar);
|
||||
}
|
||||
|
||||
OpeningCharacters = charSet.ToArray();
|
||||
Array.Sort(OpeningCharacters);
|
||||
|
||||
asciiMap = new T[maxChar + 1];
|
||||
var isOpeningCharacter = new BitVector128();
|
||||
|
||||
if (maxChar >= 128)
|
||||
nonAsciiMap = new Dictionary<uint, T>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -89,24 +71,26 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
public T this[char openingChar]
|
||||
public T this[uint openingChar]
|
||||
{
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Searches for an opening character from a registered parser in the specified string.
|
||||
/// </summary>
|
||||
@@ -116,57 +100,111 @@ namespace Markdig.Helpers
|
||||
/// <returns>Index position within the string of the first opening character found in the specified text; if not found, returns -1</returns>
|
||||
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);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal unsafe struct BitVector128
|
||||
private int IndexOfOpeningCharacterNonAscii(string text, int start, int end)
|
||||
{
|
||||
fixed uint values[4];
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(char c)
|
||||
internal unsafe struct BoolVector128
|
||||
{
|
||||
private fixed bool values[128];
|
||||
|
||||
public void Set(char c)
|
||||
{
|
||||
Debug.Assert(c < 128);
|
||||
values[c] = true;
|
||||
}
|
||||
|
||||
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(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
Debug.Assert(c < 128);
|
||||
return (values[c >> 5] & (uint)1 << c) != 0;
|
||||
}
|
||||
Debug.Assert(c < 128 && IntPtr.Size == 8);
|
||||
return values[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Markdig.Parsers
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T[] GetParsersForOpeningCharacter(char openingChar)
|
||||
public T[] GetParsersForOpeningCharacter(uint openingChar)
|
||||
{
|
||||
return charMap[openingChar];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user