@@ -409,18 +409,38 @@ public override bool Equals(object? obj)
409409 private static int GetHashCode ( RedisValue x )
410410 {
411411 x = x . Simplify ( ) ;
412- return x . Type switch
412+ switch ( x . Type )
413413 {
414- StorageType . Null => - 1 ,
415- StorageType . Double => x . OverlappedValueDouble . GetHashCode ( ) ,
416- StorageType . Int64 or StorageType . UInt64 => x . _valueInt64 . GetHashCode ( ) ,
417- // Everything else - strings *and* byte/memory/sequence buffers - compares to each other "as
418- // strings" (see operator ==): e.g. "inf" the string equals "inf" the bytes. A buffer that
419- // looked numeric has already been reduced to Int64/Double by Simplify() above, so the only
420- // equality-consistent hash for what remains is the hash of the string form. (Note we must NOT
421- // hash raw bytes here: that would give byte buffers a different hash from the equal string.)
422- _ => ( ( string ) x ! ) . GetHashCode ( ) ,
423- } ;
414+ case StorageType . Null :
415+ return - 1 ;
416+ case StorageType . Double :
417+ return x . OverlappedValueDouble . GetHashCode ( ) ;
418+ case StorageType . Int64 or StorageType . UInt64 :
419+ return x . _valueInt64 . GetHashCode ( ) ;
420+ case StorageType . String :
421+ return x . RawString ( ) . GetHashCode ( ) ;
422+ }
423+
424+ // Everything else - byte/memory/sequence buffers - compares to each other (and to strings) "as
425+ // strings" (see operator ==): e.g. "inf" the bytes equals "inf" the string. Anything that looked
426+ // numeric was already reduced to Int64/Double by Simplify() above, so the equality-consistent
427+ // hash for what remains is the hash of the string form. (We must NOT hash raw bytes: that would
428+ // give byte buffers a different hash from the equal string.)
429+ #if NET
430+ // hash the decoded UTF8 chars directly, which avoids allocating a transient string; this matches
431+ // string.GetHashCode() for the equivalent text
432+ const int StackLimit = 256 ;
433+ var maxChars = x . GetMaxCharCount ( ) ;
434+ char [ ] ? leased = null ;
435+ Span < char > chars = maxChars <= StackLimit ? stackalloc char [ StackLimit ] : ( leased = ArrayPool < char > . Shared . Rent ( maxChars ) ) ;
436+ var written = x . CopyTo ( chars ) ;
437+ var hashCode = string . GetHashCode ( chars . Slice ( 0 , written ) ) ;
438+ if ( leased is not null ) ArrayPool < char > . Shared . Return ( leased ) ;
439+ return hashCode ;
440+ #else
441+ // no string.GetHashCode(ReadOnlySpan<char>) on these targets, so fall back to the string form
442+ return ( ( string ) x ! ) . GetHashCode ( ) ;
443+ #endif
424444 }
425445
426446 /// <summary>
0 commit comments