A very fast, random text string generator in C#, Part One
This is about half of a type I designed a few years ago in response to my boss's assertion that I couldn't improve on the speed of random string generators. This is a pretty handy type to have around when you want to generate a character string of a fixed length. It so happens that 6 characters can fit a "base 36" number very well into just over 231 bits (it's just a little bit bigger than int.MaxValue and so it stores the value as a uint).
What is included below is a first step. I want to show how to refactor it for improved performance and where certain design scenarios might be better; for example, repeatedly calling the CreateNew method results in repeatedly creating new RNGCryptoServiceProviders. Although I believe this class is a bit better about seeds as it incorporates hardware-dependent data, it isn't necessarily cryptographically strong. You can add some strength by passing in a single RNG to each subsequent call, for example.
This sample also only goes one way: from number to string. Next time, we'll show the reverse operation.
Note: an important inclusion:
using System.Security.Cryptography;
/// <summary>
/// Represents a number that may be transitioned to a six-digit alphanumeric (base-36) representation. This type is not CLS-compliant.
/// </summary>
[CLSCompliant(false)]
public struct AlphaNumericNumber
{
private const uint MAX_VALUE = 2176782336; // = RADIX ^ 6
private const int RADIX = 36; // 10 digits + 26 alphabetics
private uint m_val;
/// <summary>
/// Creates a new <see>AlphaNumericNumber</see> with the specified value.
/// </summary>
/// <param name="value">The value with which to assign the number.</param>
public AlphaNumericNumber(uint value)
{
if (value > MAX_VALUE)
value %= MAX_VALUE;
m_val = value;
}
/// <summary>
/// Creates a new, random <see>AlphaNumericNumber</see>.
/// </summary>
/// <returns>A randomly-chosen <see>AlphaNumericNumber</see>.</returns>
public static AlphaNumericNumber CreateNew()
{
byte[] container = new byte[4];
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
rng.GetNonZeroBytes(container);
}
return new AlphaNumericNumber(BitConverter.ToUInt32(container, 0));
}
/// <inheritdoc />
public override string ToString()
{
char[] result = new char[6];
uint msd = RADIX * RADIX * RADIX * RADIX * RADIX;
uint currentDigit = msd;
uint accumulator = m_val;
unchecked
{
for (int charIndex = 0; charIndex < result.Length; charIndex++)
{
uint currentDigitValue = accumulator / currentDigit;
accumulator -= (currentDigitValue * currentDigit);
if (currentDigitValue <= 9)
{
result[charIndex] = (char)('0' + currentDigitValue);
}
else
{
currentDigitValue -= 10; // adjust for 'A' equaling 10.
result[charIndex] = (char)('A' + currentDigitValue);
}
currentDigit /= RADIX;
}
}
return new string(result);
}
/// <summary>
/// Gets the value contained by this <see>AlphaNumericNumber</see>.
/// </summary>
public uint Value
{
get { return m_val; }
}
}
