Skip to content

Instantly share code, notes, and snippets.

@jeriley
Created February 28, 2017 16:56
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeriley/36b29f7c46527af4532aaf092c90dd56 to your computer and use it in GitHub Desktop.
Save jeriley/36b29f7c46527af4532aaf092c90dd56 to your computer and use it in GitHub Desktop.
Salesforce ID checksum in C#
public class Checksum
{
private Dictionary<string, char> _lookup = new Dictionary<string, char>();
private char[] _checksumChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345".ToCharArray();
public Checksum()
{
for (var i = 0; i < 32; i++)
{
var bit = Convert.ToString(i, 2).PadLeft(5, '0');
_lookup.Add(bit, _checksumChars[i]);
}
}
public bool ValidCheckSum(string salesforceId)
{
if (salesforceId.Length != 18)
return false;
var chunks = new[]
{
salesforceId.Substring(0, 5),
salesforceId.Substring(5, 5),
salesforceId.Substring(10, 5)
};
var checksum = salesforceId.Substring(15, 3);
var calculatedChecksum = string.Empty;
foreach (var chunk in chunks)
{
var reversed = chunk.Reverse().ToArray();
var resultString = "";
for (var i = 0; i < reversed.Count(); i++)
{
if (char.IsUpper(reversed[i]))
resultString += 1;
else resultString += 0;
}
calculatedChecksum += _lookup[resultString];
}
return checksum == calculatedChecksum;
}
}
[TestFixture]
public class SalesforceChecksumTests
{
private Checksum _testClass = new Checksum();
[Test]
[TestCase("a0T5000000OV7X7EAL")]
[TestCase("a0Q5000000IdZj4EAF")]
[TestCase("a0Q5000000BlbfcEAB")]
[TestCase("a0Q5000000J7vZwEAJ")]
public void valid_salesforce_Id(string id)
{
_testClass.ValidCheckSum(id).ShouldBeTrue();
}
[Test]
[TestCase("a0T5000000", Description = "too short")]
[TestCase("a0T5000000123512dsadf", Description = "too long")]
[TestCase("a0T5000000OV7X7EAX", Description = "Invalid checksum")]
public void InvalidChecksums(string id)
{
_testClass.ValidCheckSum(id).ShouldBeFalse();
}
}
@tsahi
Copy link

tsahi commented Aug 2, 2023

The performance here could probably be improved if _lookup was a Dictionary<byte, char>, and then you used the 5 lower bits in a byte as the key, turning flags on whenever you hit an upper case. This can be done with a bitwise OR on the key, and shift-left on the flag:

byte key = 0;
byte flag = 16;
foreach (char c in reversed)
{
   if (Char.IsUpper(c))
   {
       key |= flag;
    }
    flag = (byte)(flag >> 1);
}

@jeriley
Copy link
Author

jeriley commented Aug 22, 2023

@tsahi write a test for it using what's above and what you have to prove it out and I'll add it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment