-
-
Save HellBrick/237cabb59af1307ff759316a9a0b381d to your computer and use it in GitHub Desktop.
HTML tag lookup generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text; | |
using System.Threading.Tasks; | |
using AngleSharp.Dom; | |
namespace AngleSharp.Performance.Playground | |
{ | |
internal static class TagLookupGenerator | |
{ | |
private const string _builderVar = "builder"; | |
public static string Generate() | |
{ | |
const int defaultIndent = 3; | |
StringBuilder builder = new StringBuilder(); | |
var tagsByLength | |
= typeof( TagNames ) | |
.GetFields( BindingFlags.Public | BindingFlags.Static ) | |
.Select( f => new Tag { Name = f.Name, Value = f.GetValue( null ) as string } ) | |
.GroupBy( f => f.Value.Length ) | |
.OrderBy( g => g.Key ); | |
builder.Indent( defaultIndent ).AppendLine( "switch (builder.Length)" ); | |
builder.Indent( defaultIndent ).AppendLine( "{" ); | |
foreach ( var tagLengthGroup in tagsByLength ) | |
{ | |
builder.Indent( defaultIndent + 1 ).AppendLine( $"case {tagLengthGroup.Key}:" ); | |
AppendTagGroupSelector( builder, defaultIndent + 2, tagLengthGroup.ToArray() ); | |
} | |
builder.Indent( defaultIndent + 1 ).AppendLine( "default:" ); | |
builder.Indent( defaultIndent + 2 ).AppendLine( "return null;" ); | |
builder.Indent( defaultIndent ).AppendLine( "}" ); | |
return builder.ToString(); | |
} | |
public static StringBuilder Indent( this StringBuilder builder, int indentCount ) => builder.Append( ' ', 4 * indentCount ); | |
private static void AppendTagGroupSelector( StringBuilder builder, int indent, Tag[] tags ) | |
{ | |
if ( tags.Length == 1 ) | |
{ | |
string tagField = $"{nameof( TagNames )}.{tags[ 0 ].Name}"; | |
builder.Indent( indent ).AppendLine( $"return CharsAreEqual({_builderVar}, {tagField}) ? {tagField} : null;" ); | |
} | |
else | |
{ | |
int mostDifferentCharIndex = GetMostDifferentCharIndex( tags ); | |
builder.Indent( indent ).AppendLine( $"switch ({_builderVar}[{mostDifferentCharIndex}])" ); | |
builder.Indent( indent ).AppendLine( "{" ); | |
foreach ( var charGroup in tags.GroupBy( t => t.Value[ mostDifferentCharIndex ] ) ) | |
{ | |
builder.Indent( indent + 1 ).AppendLine( $"case '{charGroup.Key}':" ); | |
if ( tags[ 0 ].Value.Length == 1 ) | |
builder.Indent( indent + 2 ).AppendLine( $"return {charGroup.Single().ToProperty()};" ); | |
else | |
AppendTagGroupSelector( builder, indent + 2, charGroup.ToArray() ); | |
} | |
builder.Indent( indent + 1 ).AppendLine( "default:" ); | |
builder.Indent( indent + 2 ).AppendLine( "return null;" ); | |
builder.Indent( indent ).AppendLine( "}" ); | |
} | |
} | |
private static int GetMostDifferentCharIndex( Tag[] tags ) | |
=> Enumerable | |
.Range( 0, tags[ 0 ].Value.Length ) | |
.Select | |
( | |
index | |
=> new | |
{ | |
Index = index, | |
DifferrentCharCount = tags.Select( t => t.Value[ index ] ).Distinct().Count() | |
} | |
) | |
.OrderByDescending( p => p.DifferrentCharCount ) | |
.ThenBy( p => p.Index ) | |
.Select( p => p.Index ) | |
.FirstOrDefault(); | |
private class Tag : IEquatable<Tag> | |
{ | |
public string Name { get; set; } | |
public string Value { get; set; } | |
public string ToProperty() => $"{nameof( TagNames )}.{Name}"; | |
public override string ToString() => $"{{{nameof( Name )}: {Name}. {nameof( Value )}: {Value}}}"; | |
public override int GetHashCode() | |
{ | |
unchecked | |
{ | |
const int prime = -1521134295; | |
int hash = nameof( Tag ).GetHashCode(); | |
hash = hash * prime + EqualityComparer<string>.Default.GetHashCode( Name ); | |
hash = hash * prime + EqualityComparer<string>.Default.GetHashCode( Value ); | |
return hash; | |
} | |
} | |
public bool Equals( Tag other ) => !ReferenceEquals( other, null ) && Name == other.Name && Value == other.Value; | |
public override bool Equals( object obj ) => Equals( obj as Tag ); | |
public static bool operator ==( Tag x, Tag y ) => ReferenceEquals( x, y ) || !ReferenceEquals( x, null ) && x.Equals( y ); | |
public static bool operator !=( Tag x, Tag y ) => !( x == y ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment