Skip to content

Commit 6cdfd1f

Browse files
committed
Faster enum -> string resolution
Specific enum GetStringValue extension methods for known enums Caching of generic method of resolving string values for enums based on enum type. Renamed KnownEnums.Resolve to GetStringValue and made an extension method. Added GetStringValue extension method for HttpMethod - this is a hot path, called on every request. Fixed casing issue in HttpConnection for setting ContentLength Closes #2400
1 parent 8196a60 commit 6cdfd1f

File tree

24 files changed

+860
-701
lines changed

24 files changed

+860
-701
lines changed
Lines changed: 115 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
@using System
12
@using System.Collections.Generic
23
@using System.Linq
34
@using ApiGenerator.Domain
45
@using ApiGenerator
5-
66
@functions {
77
private const string RawSize = "Raw";
88
private const string SizeEnum = "Size";
@@ -14,75 +14,149 @@
1414
}
1515
private string CreateCase(string e, string o)
1616
{
17-
var enumValue = (e == SizeEnum && o == string.Empty) ? RawSize : o.ToPascalCase(true);
17+
var enumValue = GetEnumValue(e, o);
1818
return string.Format("case {0}.{1}: return \"{2}\";", e, enumValue, o);
1919
}
2020
private bool IsFlag(string name)
2121
{
22-
return (name.EndsWith("Metric")) || name.EndsWith("Feature");
22+
return name.EndsWith("Metric") || name.EndsWith("Feature");
23+
}
24+
25+
private string CreateEnumKeyValue(string enumName, string value, int index)
26+
{
27+
var enumValue = GetEnumValue(enumName, value);
28+
return string.Format("{3}{{ {0}.{1}, \"{2}\" }},", enumName, enumValue, value, index == 0 ? "\t\t\t\t" : string.Empty);
29+
}
30+
31+
private string GetEnumValue(string enumName, string value)
32+
{
33+
return enumName == SizeEnum && value == string.Empty
34+
? RawSize
35+
: value.ToPascalCase(true);
2336
}
2437
}
2538
using System;
2639
using System.Collections.Generic;
40+
using System.Collections.Concurrent;
2741
using System.Linq;
2842
using System.Text;
43+
using System.Reflection;
2944
using System.Runtime.Serialization;
3045

3146
///This file contains all the typed enums that the client rest api spec exposes.
3247
///This file is automatically generated from https://github.com/elastic/elasticsearch/tree/@Model.Commit/rest-api-spec
3348
///Generated of commit @Model.Commit
3449
namespace Elasticsearch.Net
3550
{
36-
@foreach (EnumDescription e in Model.EnumsInTheSpec)
37-
{
38-
var isFlag = IsFlag(e.Name);
51+
@foreach (EnumDescription e in Model.EnumsInTheSpec)
52+
{
53+
var isFlag = IsFlag(e.Name);
3954
<text>
40-
@(isFlag ? "[Flags]" : string.Empty)public enum @e.Name
55+
@(isFlag ? "[Flags]" : string.Empty)public enum @e.Name
4156
{
42-
@Raw(string.Join(",\r\n\t\t", e.Options.OrderBy(s=>s == "_all" ? 1 : 0).Select((s, i) => CreateEnum(e.Name, s, isFlag ? (int?)i : null ))))
43-
}
44-
</text>
45-
}
57+
@Raw(string.Join(",\r\n\t\t", e.Options.OrderBy(s => s == "_all" ? 1 : 0).Select((s, i) => CreateEnum(e.Name, s, isFlag ? (int?)i : null))))
58+
}</text>
59+
}
4660

4761
public static class KnownEnums
62+
{
63+
private class EnumDictionary : @(Raw("Dictionary<Enum, string>"))
64+
{
65+
public EnumDictionary(int capacity) : base(capacity) {}
66+
public @(Raw("Func<Enum, string>")) Resolver { get; set; }
67+
}
68+
69+
@foreach (EnumDescription e in Model.EnumsInTheSpec)
4870
{
49-
public static string UnknownEnum { get; } = "_UNKNOWN_ENUM_";
50-
public static string Resolve(Enum e)
71+
var isFlag = IsFlag(e.Name);
72+
<text>
73+
public static string GetStringValue(this @(e.Name) enumValue)
74+
{
75+
</text>
76+
if (isFlag)
77+
{
78+
var allOption = e.Options.FirstOrDefault(o => o == "_all");
79+
if (allOption != null)
80+
{
81+
<text>if ((enumValue & @(e.Name).All) != 0) return "_all";</text>
82+
}
83+
<text>var list = new @(Raw("List<string>()"));</text>
84+
foreach (var option in e.Options.Where(o => o != "_all"))
85+
{
86+
<text>if ((enumValue & @(e.Name).@(GetEnumValue(e.Name, option))) != 0) list.Add("@(option)");</text>
87+
}
88+
<text>return string.Join(",", list);
89+
}</text>
90+
}
91+
else
92+
{
93+
<text>switch (enumValue)
94+
{
95+
@Raw(string.Join("\r\n\t\t\t\t", e.Options.Select(o => CreateCase(e.Name, o))))
96+
}
97+
throw new ArgumentException($"'{enumValue.ToString()}' is not a valid value for enum '@(e.Name)'");
98+
}</text>
99+
}
100+
}
101+
102+
private static readonly @(Raw("ConcurrentDictionary<Type, Func<Enum, string>>")) EnumStringResolvers =
103+
new @(Raw("ConcurrentDictionary<Type, Func<Enum, string>>"))();
104+
105+
static KnownEnums()
106+
{
107+
@foreach (EnumDescription e in Model.EnumsInTheSpec)
108+
{
109+
<text>EnumStringResolvers.TryAdd(typeof(@(e.Name)), (e) => GetStringValue((@(e.Name))e));</text>
110+
}
111+
}
112+
113+
public static string GetStringValue(this Enum e)
114+
{
115+
var type = e.GetType();
116+
var resolver = EnumStringResolvers.GetOrAdd(type, GetEnumStringResolver);
117+
return resolver(e);
118+
}
119+
120+
private static @Raw("Func<Enum, string>") GetEnumStringResolver(Type type)
51121
{
52-
@foreach (EnumDescription e in Model.EnumsInTheSpec)
122+
var values = Enum.GetValues(type);
123+
var dictionary = new EnumDictionary(values.Length);
124+
125+
for (int index = 0; index < values.Length; index++)
53126
{
54-
var isFlag = IsFlag(e.Name);
55-
<text>if (e is @e.Name)
56-
{ </text>
57-
if (isFlag)
127+
var value = values.GetValue(index);
128+
#if DOTNETCORE
129+
var info = type.GetTypeInfo().GetDeclaredField(value.ToString());
130+
#else
131+
var info = type.GetField(value.ToString());
132+
#endif
133+
var da = (EnumMemberAttribute[])info.GetCustomAttributes(typeof(EnumMemberAttribute), false);
134+
var stringValue = da.Length > 0 ? da[0].Value : Enum.GetName(type, value);
135+
dictionary.Add((Enum)value, stringValue);
136+
}
137+
138+
#if DOTNETCORE
139+
var isFlag = type.GetTypeInfo().GetCustomAttributes(typeof(FlagsAttribute), false).Any();
140+
#else
141+
var isFlag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
142+
#endif
143+
144+
return (e) =>
58145
{
59-
<text>var list = new @(Raw("List<string>()"));</text>
60-
foreach(var option in e.Options.OrderBy(s=>s == "_all" ? 1 : 0))
146+
if (isFlag)
61147
{
62-
if (option != "_all")
148+
var list = new @(Raw("List<string>()"));
149+
foreach(var kv in dictionary)
63150
{
64-
<text>if (e.HasFlag(@(e.Name).@(option.ToPascalCase(true)))) list.Add("@(option)");</text>
65-
}
66-
else
67-
{
68-
<text>if (e.HasFlag(@(e.Name).@(option.ToPascalCase(true)))) return "@(option)";</text>
151+
if (e.HasFlag(kv.Key)) list.Add(kv.Value);
69152
}
153+
return string.Join(",", list);
70154
}
71-
<text>return string.Join(",", list);</text>
72-
}
73-
else
74-
{
75-
<text>switch((@e.Name)e)
155+
else
76156
{
77-
@Raw(string.Join("\r\n\t\t\t\t\t", e.Options.Select(o =>CreateCase(e.Name,o))))
78-
}</text>
79-
}
80-
<text>
81-
}
82-
</text>
83-
}
84-
return UnknownEnum;
157+
return dictionary[e];
158+
}
159+
};
85160
}
86161
}
87-
}
88-
162+
}

src/Elasticsearch.Net/Connection/HttpConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ protected virtual HttpWebRequest CreateWebRequest(RequestData requestData)
6666
//see: https://github.com/elasticsearch/elasticsearch-net/issues/562
6767
var m = requestData.Method.GetStringValue();
6868
request.Method = m;
69-
if (m != "head" && m != "get" && (requestData.PostData == null))
69+
if (m != "HEAD" && m != "GET" && (requestData.PostData == null))
7070
request.ContentLength = 0;
7171

7272
return request;

src/Elasticsearch.Net/Connection/HttpMethod.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ public enum HttpMethod
1717
[EnumMember(Value = "HEAD")]
1818
HEAD
1919
}
20-
}
20+
}

0 commit comments

Comments
 (0)