Skip to content

Instantly share code, notes, and snippets.

@HellBrick
Created December 2, 2016 17:42
Show Gist options
  • Save HellBrick/237cabb59af1307ff759316a9a0b381d to your computer and use it in GitHub Desktop.
Save HellBrick/237cabb59af1307ff759316a9a0b381d to your computer and use it in GitHub Desktop.
HTML tag lookup generator
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