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; } } }
Leave a comment