DEV Community

Cover image for Base64 decode explained
Adam K Dean
Adam K Dean

Posted on • Edited on

3

Base64 decode explained

Originally posted on May 2nd, 2016 (more info)

Part 1 – base64 encode explained
Part 2 – base64 decode explained

I've used base64 a lot but never have I delved into it enough to understand exactly what goes on. So I took the time to explain via inline comments. I hope you enjoy reading it as much as I enjoyed writing it.

String.prototype.fromBase64 = function () {
  const base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef' +
                      'ghijklmnopqrstuvwxyz0123456789+/';
  let result = '',
      encoded = '';

  // step 1. convert base64chars into reverse lookup object
  const base64inv = {};
  for (let i = 0; i < base64chars.length; i++)
    base64inv[base64chars[i]] = i;

  // step 2. remove any characters that are not base64 or padding
  const base64regex = new RegExp(`[^${base64chars}=]`, 'g');
  encoded = this.replace(base64regex, '');

  // step 3. replace padding at the end with A (remember, A equals zero)
  const onePadding = encoded.charAt(encoded.length - 1) === '=';
  const twoPadding = encoded.charAt(encoded.length - 2) === '=';  
  const padding = onePadding ? twoPadding ? 'AA' : 'A' : '';
  encoded = encoded.substring(0, encoded.length - padding.length) + padding;

  // step 4. iterate over the encoded string, four characters at a time
  for (let i = 0; i < encoded.length; i += 4) {

    // step 5. convert the four base64 characters into 6-bit numbers using
    //         the base64 character -> 6-bit number map above
    const dn = base64inv[encoded.charAt(i)];
    const en = base64inv[encoded.charAt(i + 1)];
    const fn = base64inv[encoded.charAt(i + 2)];
    const gn = base64inv[encoded.charAt(i + 3)];

    // step 6. convert these four 6-bit numbers into  one 24-bit number
    //
    // if you remember from before, we split a 24-bit number into four 6-bit numbers:
    //
    // e.g.   00001111 00000101 00001010
    // to     |----||- ---||--- -||----|
    //         d     e      f      g
    //
    // d =    00000000 00000000 00000011
    // e =    00000000 00000000 00110000
    // f =    00000000 00000000 00010100
    // g =    00000000 00000000 00001010
    //
    //
    // we need to left shift them (<<) so that they all line up
    // 
    // 00000000 00000000 00XXXXXX (we have four of these)
    // DDDDDDEE EEEEFFFF FFGGGGGG (we want one of these)        
    const d = dn << 18;      // DDDDDD00 00000000 00000000
    const e = en << 12;      // 000000EE EEEE0000 00000000
    const f = fn << 6;       // 00000000 0000FFFF FF000000
    const g = gn;            // 00000000 00000000 00GGGGGG
    const n = d + e + f + g; // DDDDDDEE EEEEFFFF FFGGGGGG (yay!)

    // step 7. split this 24-bit number into three 8-bit (ASCII) characters
    //
    // if you remember, we had three of these (8-bit): 00000000
    // and we actually wanted one of these (24-bit): 00000000 00000000 00000000
    //
    // to get this, we shift the first number 16 bits left, and the second, 8 bits left:
    //
    // 00000000 <------- -------- first char << 16
    //          00000000 <------- second char << 8
    //                   00000000 third char (no shift)
    //
    // so now we want to reverse this and reclaim our three 8-bit (ASCII) characters,
    // we can do this by shifting the numbers back over to the right, and applying a
    // 255 value logical AND (&) bit mask to ignore anything in the 16 bits on the left
    //
    // e.g.   00001111 00000101 00001010
    // to     |------| |------| |------|
    //         a        b        c
    //
    // >>> 16 00000000 00000000 00001111
    // & 255  00000000 00000000 11111111
    // a =    00000000 00000000 00001111
    //                            
    // >>> 8  00000000 00001111 00000101
    // & 255  00000000 00000000 11111111
    // b =    00000000 00000000 00000101
    //
    // noop   00001111 00000101 00001010
    // & 255  00000000 00000000 11111111
    // c =    00000000 00000000 00001010
    const a = (n >>> 16) & 255;
    const b = (n >>> 8) & 255;
    const c = n & 255;

    // step 8. turn these three 8-bit numbers into ASCII characters, and append to result
    result += String.fromCharCode(a, b, c);
  }

  // step 8. finally, remove any padding that was previously added to make this a multiple of 3
  return result.substring(0, result.length - padding.length);
};
Enter fullscreen mode Exit fullscreen mode

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more