Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Commit

Permalink
Optimize random string generator
Browse files Browse the repository at this point in the history
  • Loading branch information
hhvrc committed Jul 20, 2023
1 parent 0599a03 commit 9833f28
Showing 1 changed file with 51 additions and 21 deletions.
72 changes: 51 additions & 21 deletions Common/Utils/StringUtils.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,67 @@
using System.Security.Cryptography;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

namespace ZapMe.Utils;

public static class StringUtils
{
/// <summary>
/// Generates a cryptographically secure random string of <paramref name="length"/> length, consisting of these characters: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
/// </summary>
/// <param name="length">Length of the generated string</param>
/// <returns></returns>
public static string GenerateUrlSafeRandomString(int length)
private static void FillUrlSafeRandomString(Span<char> buffer, int length)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";

Span<byte> data = stackalloc byte[(length * 3 / 4) + 4];

using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
int[] array = ArrayPool<int>.Shared.Rent((length / 5) + 1);
try
{
rng.GetBytes(data);
}
Span<int> data = new(array);

Span<char> buffer = stackalloc char[length + 3];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(MemoryMarshal.AsBytes(data));
}

for (int i = 0, j = 0; j < length; i += 3, j += 4)
{
int bitBuffer = (data[i + 2] << 16) | (data[i + 1] << 8) | data[i];
int i = 0;
int j = 0;
do
{
int bitBuffer = data[i++];

buffer[j + 0] = chars[bitBuffer & 0x3F];
buffer[j + 1] = chars[(bitBuffer >> 6) & 0x3F];
buffer[j + 2] = chars[(bitBuffer >> 12) & 0x3F];
buffer[j + 3] = chars[(bitBuffer >> 18) & 0x3F];
buffer[j + 4] = chars[(bitBuffer >> 24) & 0x3F];

j += 5;
}
while ((j + 5) < length);

buffer[j + 0] = chars[bitBuffer & 0x3F];
buffer[j + 1] = chars[(bitBuffer >> 6) & 0x3F];
buffer[j + 2] = chars[(bitBuffer >> 12) & 0x3F];
buffer[j + 3] = chars[bitBuffer >> 18];
int remainder = length - j;
if (remainder == 0)
{
return;
}

int lastBitBuffer = data[i];
do
{
buffer[j++] = chars[(lastBitBuffer >> (--remainder * 6)) & 0x3F];
}
while (remainder > 0);
}
finally
{
ArrayPool<int>.Shared.Return(array);
}
}

return new string(buffer[..length]);
/// <summary>
/// Generates a cryptographically secure random string of <paramref name="length"/> length, consisting of these characters: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
/// </summary>
/// <param name="length">Length of the generated string</param>
/// <returns></returns>
public static string GenerateUrlSafeRandomString(int length)
{
return String.Create(length, length, FillUrlSafeRandomString);
}
}

0 comments on commit 9833f28

Please sign in to comment.