The standard Base64 encoding algorithm as defined in RFC 4648 processes input in sequential 3-byte blocks. But what happens if the input length is not an exact multiple of 3? This is where padding comes in.
Handling Remainder Bytes & Padding
When dividing the total input bytes by 3, there are three possible outcomes for the final block:
Case 1: No Remainder (0 bytes left over)
The input byte length is divisible by 3. Every 3-byte block converts to 4 Base64 characters. No padding characters (=) are appended.
Case 2: 1 Byte Remaining
There are only 8 bits available in the final block:
- The first 6 bits form the first Base64 index.
- The remaining 2 bits are padded on the right with four zero bits (
0000) to complete a 6-bit index. - Because 2 characters out of 4 are missing, two equals signs (
==) are appended to complete the 4-character quantum.
Example: A (ASCII 65 = 01000001) → indices 16 (Q) and 16 (Q) + == = QQ==.
Case 3: 2 Bytes Remaining
There are 16 bits available in the final block:
- The first 6 bits form index 1.
- The next 6 bits form index 2.
- The remaining 4 bits are padded with two zero bits (
00) to form index 3. - One equals sign (
=) is appended to complete the 4-character block.
Example: AB → QUI=.
Pseudocode Implementation
function encodeBase64(bytes):
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
let output = ""
for i from 0 to bytes.length step 3:
b0 = bytes[i]
b1 = bytes[i+1] or 0
b2 = bytes[i+2] or 0
triple = (b0 << 16) | (b1 << 8) | b2
output += alphabet[(triple >> 18) & 0x3F]
output += alphabet[(triple >> 12) & 0x3F]
output += (i + 1 < bytes.length) ? alphabet[(triple >> 6) & 0x3F] : "="
output += (i + 2 < bytes.length) ? alphabet[triple & 0x3F] : "="
return output