@@ -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