0

While reading further about Salesforce Ids, I came across this article detailing how three extra characters are appended to the original 15-character record Id in order to identify the casing of each of its characters. It is a pretty straightforward algorithm:

  1. Divide the original Id string into three 5-character chunks.
  2. Reverse the order of the characters in each chunk.
  3. Replace each character with 1 if it is an uppercase letter and with 0 otherwise, essentially turning each chunk into a 5-bit binary number.
  4. Lookup the chunk numbers against a table of characters representing every combination.
  5. Append the characters to the original Id.

The reasoning behind each step is very clear to me, except for step 2: reverse the characters in the chunk. Unless I am overlooking something, the algorithm would work just fine without it; the only difference being the order of the appended three characters in the 18-bit Id.

Does anyone know the reason why they chose to include this extra step when creating 18-character Ids?

1 Answer 1

1

This is speculation, not an official answer. I work for Salesforce, but I didn't write the algorithm and have no idea what went through the heads of those who did.

The step (2) as shown is not, in my opinion, a very good expression of how the algorithm works. Here's another way I've implemented it (in Python) that shows that there's not really a step that is a logical reversal. There's a transformation that can be seen as a reversal, but the use of an explicit reversal step is really an artifact of how the blog author has chosen to describe and implement the algorithm. That implementation is in my eyes wildly inefficient, using strings of 1s and 0s rather than actual binary numbers!

def to18(idstr):
    if len(idstr) == 15:
        # To construct the 18 character Salesforce id, we're going to build a 15-bit checksum
        # expressed as three alphanumeric characters.
        # We do this by constructing 3 five-bit indices into the alnum character range
        # Each five-bit index corresponds to the case (1 = uppercase) of five characters
        # of the 15-digit Salesforce Id, in reverse order (LSB is the first character)

        # Build up a bitstring
        bitstring = 0
        for i in range(0, 15):
            if idstr[i] >= 'A' and idstr[i] <= 'Z':
                bitstring |= 1 << i

        # Take three slices of the bitstring and use them as 5-bit indices into the alnum sequence.
        alnums = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'
        return idstr + alnums[bitstring & 0x1F] + alnums[bitstring>>5 & 0x1F] + alnums[bitstring>>10]

As you can see, this implementation is arguably more direct. Rather than building three independent 5-digit bitstrings, we build one 15-digit bitstring, where each bit corresponds to the case of a character in the original id. We're also using actual numbers, rather than strings, here.

As we loop over the characters, starting at index zero, we bitwise-OR a 1 into the bitstring to set the bit at the "first" position. In this case, the first position is the rightmost bit, even though we have the leftmost character.

This approach is just more natural because of the fact that the least significant bit of an integer is "rightmost" when represented visually.

If we tried to mirror the positions of the bits and characters so that they'd like up visually, the code would be a little less natural to read:

        bitstring = 0
        for i in range(0, 15):
            if idstr[i] >= 'A' and idstr[i] <= 'Z':
                bitstring |= 1 << (15-i) # Note that we have to calculate the index here!

(Or we could iterate the characters backwards).

Note also the reason we use 5-bit bitstring slices: 25 = 32, meaning we can use the 26 letters plus first six digits, which are legal in a Salesforce id already, and it covers the entire 15-bit string. If we used more bits, we could not actually reduce the length of the checksum. We could represent 15 bits in two characters if we used 8-bit strings (28 = 256 values), but since Id values are base62, we would then have to re-encode those values into a smaller character set. If we used fewer bits, we would need more characters: 4 bits would require 4 characters for the checksum.

So long story short - I suspect the reason the algorithm works the way it does is simply that it's natural to implement it that way, and there's no intent to do a "reversal" step as such.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .