1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 using System
.Diagnostics
;
7 using System
.Runtime
.InteropServices
;
8 using System
.Text
.Unicode
;
9 using Internal
.Runtime
.CompilerServices
;
11 #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
13 using nuint
= System
.UInt64
;
15 using nuint
= System
.UInt32
;
20 internal static partial class Marvin
23 /// Compute a Marvin OrdinalIgnoreCase hash and collapse it into a 32-bit hash.
24 /// n.b. <paramref name="count"/> is specified as char count, not byte count.
26 public static int ComputeHash32OrdinalIgnoreCase(ref char data
, int count
, uint p0
, uint p1
)
28 uint ucount
= (uint)count
; // in chars
29 nuint byteOffset
= 0; // in bytes
32 // We operate on 32-bit integers (two chars) at a time.
36 tempValue
= Unsafe
.ReadUnaligned
<uint>(ref Unsafe
.As
<char, byte>(ref Unsafe
.AddByteOffset(ref data
, byteOffset
)));
37 if (!Utf16Utility
.AllCharsInUInt32AreAscii(tempValue
))
41 p0
+= Utf16Utility
.ConvertAllAsciiCharsInUInt32ToUppercase(tempValue
);
42 Block(ref p0
, ref p1
);
48 // We have either one char (16 bits) or zero chars left over.
49 Debug
.Assert(ucount
< 2);
53 tempValue
= Unsafe
.AddByteOffset(ref data
, byteOffset
);
54 if (tempValue
> 0x7Fu
)
59 // addition is written with -0x80u to allow fall-through to next statement rather than jmp past it
60 p0
+= Utf16Utility
.ConvertAllAsciiCharsInUInt32ToUppercase(tempValue
) + (0x800000u
- 0x80u
);
64 Block(ref p0
, ref p1
);
65 Block(ref p0
, ref p1
);
67 return (int)(p1 ^ p0
);
70 Debug
.Assert(ucount
<= int.MaxValue
); // this should fit into a signed int
71 return ComputeHash32OrdinalIgnoreCaseSlow(ref Unsafe
.AddByteOffset(ref data
, byteOffset
), (int)ucount
, p0
, p1
);
74 private static int ComputeHash32OrdinalIgnoreCaseSlow(ref char data
, int count
, uint p0
, uint p1
)
76 Debug
.Assert(count
> 0);
78 char[]? borrowedArr
= null;
79 Span
<char> scratch
= (uint)count
<= 64 ? stackalloc char[64] : (borrowedArr
= ArrayPool
<char>.Shared
.Rent(count
));
81 int charsWritten
= new ReadOnlySpan
<char>(ref data
, count
).ToUpperInvariant(scratch
);
82 Debug
.Assert(charsWritten
== count
); // invariant case conversion should involve simple folding; preserve code unit count
84 // Slice the array to the size returned by ToUpperInvariant.
85 // Multiplication below will not overflow since going from positive Int32 to UInt32.
86 int hash
= ComputeHash32(ref Unsafe
.As
<char, byte>(ref MemoryMarshal
.GetReference(scratch
)), (uint)charsWritten
* 2, p0
, p1
);
88 // Return the borrowed array if necessary.
89 if (borrowedArr
!= null)
91 ArrayPool
<char>.Shared
.Return(borrowedArr
);