Skip to content

Commit 23f22bd

Browse files
Slight improvement.
1 parent 1038f64 commit 23f22bd

1 file changed

Lines changed: 76 additions & 84 deletions

File tree

Source/EnumValue.cs

Lines changed: 76 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -138,29 +138,87 @@ static Expression CreateNewTuple(bool success, TEnum value)
138138
=> LazyInitializer.EnsureInitialized(ref _valueLookup,
139139
() => GetEnumTryParseDelegate())!;
140140

141-
static (string Name, TEnum Value)[]?[] CreateLookup()
141+
static Entry[]?[] CreateLookup()
142142
{
143143
var longest = 0;
144-
var d = new Dictionary<int, SortedList<string, (string Name, TEnum Value)>>();
144+
var d = new Dictionary<int, List<Entry>>();
145145

146146
foreach (var e in Values)
147147
{
148148
var n = string.Intern(e.ToString());
149149
var len = n.Length;
150150
if (len > longest) longest = len;
151151
if (!d.TryGetValue(len, out var v)) d.Add(len, v = new());
152-
v.Add(n, (n, e));
152+
v.Add(new(n, e));
153153
}
154154

155-
var result = new (string Name, TEnum Value)[]?[longest + 1];
155+
var result = new Entry[]?[longest + 1];
156156
foreach (var i in d.Keys)
157-
result[i] = d[i].Values.ToArray();
157+
result[i] = d[i].OrderBy(v=>v.Name).ToArray();
158158

159159
return result;
160160
}
161161

162-
private static (string Name, TEnum Value)[]?[]? _lookup;
163-
internal static (string Name, TEnum Value)[]?[] Lookup
162+
internal readonly struct Entry
163+
{
164+
public Entry(string name, TEnum value)
165+
{
166+
Name = name;
167+
Value = value;
168+
}
169+
170+
public string Name { get; }
171+
public TEnum Value { get; }
172+
173+
public void Deconstruct(out string name, out TEnum value)
174+
{
175+
name = Name;
176+
value = Value;
177+
}
178+
179+
public static int Find(Span<Entry> span, StringSegment name, StringComparison sc)
180+
{
181+
// Small enough? Just brute force the index.
182+
var len = span.Length;
183+
if(len < 12)
184+
{
185+
for(var i = 0; i< len; i++)
186+
{
187+
ref Entry e = ref span[i];
188+
if (name.Equals(e.Name, sc))
189+
return i;
190+
}
191+
192+
return -1;
193+
}
194+
195+
// Use binary search for larger.
196+
int left = 0;
197+
int right = span.Length - 1;
198+
199+
while (left <= right)
200+
{
201+
int middle = left + (right - left) / 2;
202+
var middleKey = span[middle].Name;
203+
204+
if (right - left < 4 && name.Equals(middleKey, sc))
205+
return middle;
206+
207+
var comparison = StringSegment.Compare(middleKey, name, sc);
208+
if (comparison < 0)
209+
left = middle + 1;
210+
else if (comparison > 0)
211+
right = middle - 1;
212+
else
213+
return middle;
214+
}
215+
216+
return -1;
217+
}
218+
}
219+
220+
private static Entry[]?[]? _lookup;
221+
internal static Entry[]?[] Lookup
164222
=> LazyInitializer.EnsureInitialized(ref _lookup,
165223
() => CreateLookup())!;
166224

@@ -455,20 +513,20 @@ public static bool TryParse<TEnum>(StringSegment value, out TEnum e)
455513
/// <summary>
456514
/// Converts the string representation of the name of one or more enumerated constants to an equivalent enumerated object.
457515
/// </summary>
458-
/// <param name="value">The string representing the enum value to search for.</param>
516+
/// <param name="name">The string representing the enum name to search for.</param>
459517
/// <param name="ignoreCase">If true, will ignore case differences when looking for a match.</param>
460-
/// <param name="e">The enum that represents the string <paramref name="value"/> provided.</param>
518+
/// <param name="e">The enum that represents the string <paramref name="name"/> provided.</param>
461519
/// <returns>true if the value was found; otherwise false.</returns>
462-
public static bool TryParse<TEnum>(StringSegment value, bool ignoreCase, out TEnum e)
520+
public static bool TryParse<TEnum>(StringSegment name, bool ignoreCase, out TEnum e)
463521
where TEnum : Enum
464522
{
465-
if (!value.HasValue) goto notFound;
523+
if (!name.HasValue) goto notFound;
466524

467525
// If this is a string, use the optimized version.
468-
if (!ignoreCase && value.Buffer.Length == value.Length)
469-
return TryParse(value.Buffer, out e);
526+
if (!ignoreCase && name.Buffer.Length == name.Length)
527+
return TryParse(name.Buffer, out e);
470528

471-
var len = value.Length;
529+
var len = name.Length;
472530
if (len == 0) goto notFound;
473531
var lookup = EnumValue<TEnum>.Lookup;
474532
if (len >= lookup.Length) goto notFound;
@@ -481,78 +539,12 @@ public static bool TryParse<TEnum>(StringSegment value, bool ignoreCase, out TEn
481539
? StringComparison.OrdinalIgnoreCase
482540
: StringComparison.Ordinal;
483541

484-
if (r.Length > 64)
485-
goto binarySearch;
486-
487-
// For smaller sizes, a simple linear search is faster than a binary search.
488-
// Above arrays sizes of 64 is when things start to change and binary search wins on average.
489-
foreach (var (Name, Value) in r)
490-
{
491-
if (Name.Equals(value, sc))
492-
{
493-
e = Value;
494-
return true;
495-
}
496-
}
497-
498-
goto notFound;
499-
500-
binarySearch:
501542
var span = r.AsSpan();
502-
503-
// Use a custom binary search to optimize for potentially larger cases.
504-
search:
505-
switch (span.Length)
543+
var index = EnumValue<TEnum>.Entry.Find(span, name, sc);
544+
if(index != -1)
506545
{
507-
case 1:
508-
{
509-
var (Name, Value) = span[0];
510-
if (Name.Equals(value, sc))
511-
{
512-
e = Value;
513-
return true;
514-
}
515-
516-
break;
517-
}
518-
519-
case 2:
520-
{
521-
var (Name, Value) = span[0];
522-
if (Name.Equals(value, sc))
523-
{
524-
e = Value;
525-
return true;
526-
}
527-
528-
span = span.Slice(1, 1);
529-
goto search;
530-
}
531-
532-
default:
533-
{
534-
int i = span.Length / 2;
535-
var (Name, Value) = span[i];
536-
var comparison = StringSegment.Compare(value, Name, sc);
537-
switch (Math.Sign(comparison))
538-
{
539-
case 0:
540-
e = Value;
541-
return true;
542-
543-
case -1:
544-
span = span.Slice(0, i);
545-
goto search;
546-
547-
case +1:
548-
span = span.Slice(i + 1);
549-
goto search;
550-
}
551-
552-
Debug.Fail("Should never have arrived here");
553-
554-
break;
555-
}
546+
e = span[index].Value;
547+
return true;
556548
}
557549

558550
notFound:

0 commit comments

Comments
 (0)