Skip to content

Commit ddd4dcc

Browse files
russcamMpdreamz
authored andcommitted
Faster enum -> string resolution (#2418)
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 Conflicts: src/CodeGeneration/ApiGenerator/Views/Enums.Generated.cshtml src/Elasticsearch.Net/Domain/Enums.Generated.cs src/Elasticsearch.Net/Extensions/Extensions.cs src/Nest/Mapping/AttributeBased/ElasticsearchCorePropertyAttributeBase.cs src/Nest/Mapping/Types/Core/Number/NumberAttribute.cs
1 parent 64d9a7e commit ddd4dcc

File tree

27 files changed

+575
-411
lines changed

27 files changed

+575
-411
lines changed
Lines changed: 123 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,162 @@
1+
@using System
12
@using System.Collections.Generic
23
@using System.Linq
34
@using ApiGenerator.Domain
45
@using ApiGenerator
5-
66
@functions {
7-
private string CreateEnum(string o, int? i)
7+
private const string RawSize = "Raw";
8+
private const string SizeEnum = "Size";
9+
10+
private string CreateEnum(string enumName, string value, int? i)
811
{
9-
return string.Format("[EnumMember(Value = \"{0}\")]\r\n\t\t{1}{2}", o, o.ToPascalCase(true), i.HasValue ? " = 1 << " + i.Value : null);
12+
var enumValue = (enumName == SizeEnum && value == string.Empty) ? RawSize : value.ToPascalCase(true);
13+
return string.Format("[EnumMember(Value = \"{0}\")]\r\n\t\t{1}{2}", value, enumValue, i.HasValue ? " = 1 << " + i.Value : null);
1014
}
1115
private string CreateCase(string e, string o)
1216
{
13-
return string.Format("case {0}.{1}: return \"{2}\";", e, o.ToPascalCase(true), o);
17+
var enumValue = GetEnumValue(e, o);
18+
return string.Format("case {0}.{1}: return \"{2}\";", e, enumValue, o);
1419
}
1520
private bool IsFlag(string name)
1621
{
17-
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);
1836
}
1937
}
2038
using System;
2139
using System.Collections.Generic;
40+
using System.Collections.Concurrent;
2241
using System.Linq;
2342
using System.Text;
43+
using System.Reflection;
2444
using System.Runtime.Serialization;
2545

2646
///This file contains all the typed enums that the client rest api spec exposes.
27-
///This file is automatically generated from https://github.com/elasticsearch/elasticsearch-rest-api-spec
47+
///This file is automatically generated from https://github.com/elastic/elasticsearch/tree/@Model.Commit/rest-api-spec
2848
///Generated of commit @Model.Commit
2949
namespace Elasticsearch.Net
3050
{
31-
@foreach (EnumDescription e in Model.EnumsInTheSpec)
32-
{
33-
var isFlag = IsFlag(e.Name);
51+
@foreach (EnumDescription e in Model.EnumsInTheSpec)
52+
{
53+
var isFlag = IsFlag(e.Name);
3454
<text>
35-
@(isFlag ? "[Flags]" : string.Empty)public enum @e.Name
55+
@(isFlag ? "[Flags]" : string.Empty)public enum @e.Name
3656
{
37-
@Raw(string.Join(",\r\n\t\t", e.Options.OrderBy(s=>s == "_all" ? 1 : 0).Select((s, i) => CreateEnum(s, isFlag ? (int?)i : null ))))
38-
}
39-
</text>
40-
}
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+
}
4160

4261
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)
4370
{
44-
public static string UnknownEnum { get; } = "_UNKNOWN_ENUM_";
45-
public static string Resolve(Enum e)
71+
var isFlag = IsFlag(e.Name);
72+
<text>
73+
public static string GetStringValue(this @(e.Name) enumValue)
4674
{
47-
@foreach (EnumDescription e in Model.EnumsInTheSpec)
75+
</text>
76+
if (isFlag)
77+
{
78+
var allOption = e.Options.FirstOrDefault(o => o == "_all");
79+
if (allOption != null)
4880
{
49-
var isFlag = IsFlag(e.Name);
50-
<text>if (e is @e.Name)
51-
{ </text>
52-
if (isFlag)
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"))
5385
{
54-
<text>var list = new @(Raw("List<string>()"));</text>
55-
foreach(var option in e.Options.OrderBy(s=>s == "_all" ? 1 : 0))
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)
121+
{
122+
var values = Enum.GetValues(type);
123+
var dictionary = new EnumDictionary(values.Length);
124+
125+
for (int index = 0; index < values.Length; index++)
126+
{
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) =>
145+
{
146+
if (isFlag)
56147
{
57-
if (option != "_all")
148+
var list = new @(Raw("List<string>()"));
149+
foreach(var kv in dictionary)
58150
{
59-
<text>if (e.HasFlag(@(e.Name).@(option.ToPascalCase(true)))) list.Add("@(option)");</text>
60-
}
61-
else
62-
{
63-
<text>if (e.HasFlag(@(e.Name).@(option.ToPascalCase(true)))) return "@(option)";</text>
151+
if (e.HasFlag(kv.Key)) list.Add(kv.Value);
64152
}
153+
return string.Join(",", list);
65154
}
66-
<text>return string.Join(",", list);</text>
67-
}
68-
else
69-
{
70-
<text>switch((@e.Name)e)
155+
else
71156
{
72-
@Raw(string.Join("\r\n\t\t\t\t\t", e.Options.Select(o =>CreateCase(e.Name,o))))
73-
}</text>
74-
}
75-
<text>
76-
}
77-
</text>
78-
}
79-
return UnknownEnum;
157+
return dictionary[e];
158+
}
159+
};
80160
}
81161
}
82-
}
83-
162+
}

src/Elasticsearch.Net/Connection/HttpConnection.cs

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

7171
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)