Last active
July 9, 2025 10:16
-
-
Save idea-lei/8b619c1b46ed2158c2920a172a644c7f to your computer and use it in GitHub Desktop.
High performance Short Id
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// ShortId in string format, preferred when a GUID is overkill. | |
/// </summary> | |
public static class ShortId | |
{ | |
/// <summary> | |
/// 32 chars (2^5). removed some chars for better readablity (i,l,o,u) | |
/// </summary> | |
private const string _chars = "0123456789abcdefghjkmnpqrstvwxyz"; | |
// If the probability of success is 50%, the probability of success in five attempts is 97%. | |
private const byte _maxTrialTimes = 5; | |
/// <summary> | |
/// Generates a short id. May be duplicated. | |
/// </summary> | |
/// <remarks> | |
/// Based on long type (64-1=63 bit due to sign), so max length is 12 (13*5=65), not recommanded for fewer than 6 chars. | |
/// </remarks> | |
/// <param name="size"> | |
/// Length of the result string. Range 6-12, 10 as default. <br/> | |
/// Default value 10 will contain 32^10 (around 10^15) possibilities <br/> | |
/// When generating 10^6 times, it will have about 1.4% chance to have duplicate elements. | |
/// </param> | |
public static string New([Range(6, 12)] byte size = 10) | |
{ | |
// max: 3FFF FFFF (62 bit), half of the long.MaxValue, but still enough for size 12 | |
long value = (long)Random.Shared.Next(int.MaxValue) * Random.Shared.Next(int.MaxValue); | |
var output = new char[size]; | |
for (int i = size - 1; i >= 0; i--) | |
{ | |
output[i] = _chars[(int)(value % 32)]; | |
value >>= 5; | |
} | |
return new string(output); | |
} | |
/// <summary> | |
/// Generates a unique short id that is different from the existing items. | |
/// </summary> | |
public static string New(IQueryable<string> existingIds, [Range(6, 12)] byte size = 10) | |
{ | |
existingIds = existingIds.Where(x => x.Length == size); | |
byte count = 0; | |
string newId = New(size); | |
while(existingIds.Contains(newId)) | |
{ | |
newId = New(size); | |
count++; | |
if (count > _maxTrialTimes) | |
throw new Exception("Too many elements in the collection to find an unique id."); | |
} | |
return newId; | |
} | |
/// <summary> | |
/// Generates a unique short id that is different from the existing items. | |
/// </summary> | |
public static string New(IEnumerable<string> existingIds, [Range(6, 12)] byte size = 10) | |
{ | |
existingIds = existingIds.Where(x => x.Length == size); | |
byte count = 0; | |
string newId = New(size); | |
while (existingIds.Contains(newId)) | |
{ | |
newId = New(size); | |
count++; | |
if (count > _maxTrialTimes) | |
throw new Exception("Too many elements in the collection to find an unique id."); | |
} | |
return newId; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment