Skip to content

Commit afd2624

Browse files
authored
Optimize Sha1ForNonSecretPurposes (#120674)
- Introduce more efficient one-shot static method and updated callers in CoreLib to use it - Optimize bounds checks and allocations Contributes to #45237
1 parent 335820f commit afd2624

File tree

3 files changed

+110
-47
lines changed

3 files changed

+110
-47
lines changed

src/libraries/Common/src/System/Sha1ForNonSecretPurposes.cs

Lines changed: 99 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Buffers.Binary;
5+
using System.Diagnostics;
46
using System.Numerics;
57

68
namespace System
@@ -17,20 +19,77 @@ internal struct Sha1ForNonSecretPurposes
1719
private uint[] _w; // Workspace
1820
private int _pos; // Length of current chunk in bytes
1921

22+
/// <summary>
23+
/// Computes the SHA1 hash of the provided data.
24+
/// </summary>
25+
/// <param name="source">The data to hash.</param>
26+
/// <param name="destination">The buffer to receive the hash value.</param>
27+
public static void HashData(ReadOnlySpan<byte> source, Span<byte> destination)
28+
{
29+
Debug.Assert(destination.Length == 20);
30+
31+
Span<uint> w = stackalloc uint[85];
32+
33+
Start(w);
34+
35+
int originalLength = source.Length;
36+
37+
while (source.Length >= 64)
38+
{
39+
for (int i = 0; i < 16; i++)
40+
{
41+
w[i] = BinaryPrimitives.ReadUInt32BigEndian(source);
42+
source = source.Slice(4);
43+
}
44+
Drain(w);
45+
}
46+
47+
Span<byte> tail = stackalloc byte[2 * 64];
48+
source.CopyTo(tail);
49+
int pos = source.Length;
50+
tail[pos++] = 0x80;
51+
while ((pos & 63) != 56)
52+
{
53+
tail[pos++] = 0x00;
54+
}
55+
BinaryPrimitives.WriteUInt64BigEndian(tail.Slice(pos), (ulong)originalLength * 8);
56+
tail = tail.Slice(0, pos + 8);
57+
58+
while (tail.Length > 0)
59+
{
60+
for (int i = 0; i < 16; i++)
61+
{
62+
w[i] = BinaryPrimitives.ReadUInt32BigEndian(tail);
63+
tail = tail.Slice(4);
64+
}
65+
Drain(w);
66+
}
67+
68+
for (int i = 80; i < w.Length; i++)
69+
{
70+
BinaryPrimitives.WriteUInt32BigEndian(destination, w[i]);
71+
destination = destination.Slice(4);
72+
}
73+
}
74+
2075
/// <summary>
2176
/// Call Start() to initialize the hash object.
2277
/// </summary>
2378
public void Start()
2479
{
25-
_w ??= new uint[85];
80+
Start(_w ??= new uint[85]);
2681

2782
_length = 0;
2883
_pos = 0;
29-
_w[80] = 0x67452301;
30-
_w[81] = 0xEFCDAB89;
31-
_w[82] = 0x98BADCFE;
32-
_w[83] = 0x10325476;
33-
_w[84] = 0xC3D2E1F0;
84+
}
85+
86+
private static void Start(Span<uint> w)
87+
{
88+
w[80] = 0x67452301;
89+
w[81] = 0xEFCDAB89;
90+
w[82] = 0x98BADCFE;
91+
w[83] = 0x10325476;
92+
w[84] = 0xC3D2E1F0;
3493
}
3594

3695
/// <summary>
@@ -77,6 +136,8 @@ public void Append(ReadOnlySpan<byte> input)
77136
/// </param>
78137
public void Finish(Span<byte> output)
79138
{
139+
Debug.Assert(output.Length == 20);
140+
80141
long l = _length + 8 * _pos;
81142
Append(0x80);
82143
while (_pos != 56)
@@ -93,12 +154,10 @@ public void Finish(Span<byte> output)
93154
Append((byte)(l >> 8));
94155
Append((byte)l);
95156

96-
int end = output.Length < 20 ? output.Length : 20;
97-
for (int i = 0; i != end; i++)
157+
for (int i = 80; i < _w.Length; i++)
98158
{
99-
uint temp = _w[80 + i / 4];
100-
output[i] = (byte)(temp >> 24);
101-
_w[80 + i / 4] = temp << 8;
159+
BinaryPrimitives.WriteUInt32BigEndian(output, _w[i]);
160+
output = output.Slice(4);
102161
}
103162
}
104163

@@ -107,53 +166,59 @@ public void Finish(Span<byte> output)
107166
/// </summary>
108167
private void Drain()
109168
{
110-
for (int i = 16; i != 80; i++)
169+
Drain(_w);
170+
_length += 512; // 64 bytes == 512 bits
171+
_pos = 0;
172+
}
173+
174+
private static void Drain(Span<uint> w)
175+
{
176+
var _ = w[84]; // Hint to eliminate bounds checks
177+
178+
for (int i = 16; i < 80; i++)
111179
{
112-
_w[i] = BitOperations.RotateLeft(_w[i - 3] ^ _w[i - 8] ^ _w[i - 14] ^ _w[i - 16], 1);
180+
w[i] = BitOperations.RotateLeft(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
113181
}
114182

115-
uint a = _w[80];
116-
uint b = _w[81];
117-
uint c = _w[82];
118-
uint d = _w[83];
119-
uint e = _w[84];
183+
uint a = w[80];
184+
uint b = w[81];
185+
uint c = w[82];
186+
uint d = w[83];
187+
uint e = w[84];
120188

121-
for (int i = 0; i != 20; i++)
189+
for (int i = 0; i < 20; i++)
122190
{
123191
const uint k = 0x5A827999;
124192
uint f = (b & c) | ((~b) & d);
125-
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + _w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
193+
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
126194
}
127195

128-
for (int i = 20; i != 40; i++)
196+
for (int i = 20; i < 40; i++)
129197
{
130198
uint f = b ^ c ^ d;
131199
const uint k = 0x6ED9EBA1;
132-
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + _w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
200+
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
133201
}
134202

135-
for (int i = 40; i != 60; i++)
203+
for (int i = 40; i < 60; i++)
136204
{
137205
uint f = (b & c) | (b & d) | (c & d);
138206
const uint k = 0x8F1BBCDC;
139-
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + _w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
207+
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
140208
}
141209

142-
for (int i = 60; i != 80; i++)
210+
for (int i = 60; i < 80; i++)
143211
{
144212
uint f = b ^ c ^ d;
145213
const uint k = 0xCA62C1D6;
146-
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + _w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
214+
uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
147215
}
148216

149-
_w[80] += a;
150-
_w[81] += b;
151-
_w[82] += c;
152-
_w[83] += d;
153-
_w[84] += e;
154-
155-
_length += 512; // 64 bytes == 512 bits
156-
_pos = 0;
217+
w[80] += a;
218+
w[81] += b;
219+
w[82] += c;
220+
w[83] += d;
221+
w[84] += e;
157222
}
158223
}
159224
}

src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,16 +1764,18 @@ private static Guid GenerateGuidFromName(string name)
17641764
0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
17651765
];
17661766

1767-
byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1768-
Sha1ForNonSecretPurposes hash = default;
1769-
hash.Start();
1770-
hash.Append(namespaceBytes);
1771-
hash.Append(bytes);
1772-
Array.Resize(ref bytes, 16);
1773-
hash.Finish(bytes);
1767+
int nameByteCount = Encoding.BigEndianUnicode.GetByteCount(name);
1768+
int totalLength = namespaceBytes.Length + nameByteCount;
1769+
Span<byte> source = totalLength <= 256 ? stackalloc byte[totalLength] : new byte[totalLength];
1770+
1771+
namespaceBytes.CopyTo(source);
1772+
Encoding.BigEndianUnicode.GetBytes(name, source.Slice(namespaceBytes.Length));
1773+
1774+
Span<byte> bytes = stackalloc byte[20];
1775+
Sha1ForNonSecretPurposes.HashData(source, bytes);
17741776

17751777
bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1776-
return new Guid(bytes);
1778+
return new Guid(bytes.Slice(0, 16));
17771779
}
17781780

17791781
private static unsafe void DecodeObjects(object?[] decodedObjects, Type[] parameterTypes, EventData* data)

src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ internal static partial class AssemblyNameHelpers
2020
throw new SecurityException(SR.Security_InvalidAssemblyPublicKey);
2121

2222
Span<byte> hash = stackalloc byte[20];
23-
24-
Sha1ForNonSecretPurposes sha1 = default;
25-
sha1.Start();
26-
sha1.Append(publicKey);
27-
sha1.Finish(hash);
23+
Sha1ForNonSecretPurposes.HashData(publicKey, hash);
2824

2925
byte[] publicKeyToken = new byte[PublicKeyTokenLength];
3026
for (int i = 0; i < publicKeyToken.Length; i++)

0 commit comments

Comments
 (0)