Showing posts with label Base64. Show all posts
Showing posts with label Base64. Show all posts

Base64 Encoder

Base64 is popular system that transforms the binary data into limited set of 64 characters.

For example, text abc can be represented in binary as 011000010110001001100011, Base64 divides this binay data into group of 6 bits 011000 010110 001001 100011 and converts it to as per the encoding table.

Base64 Encoding Table has total 64 (2^6) characters.

  • 0 to 25 : A to Z
  • 26 to 51 : a to z
  • 52 to 61 : 0 to 9
  • 62 : Plus symbol ‘+’
  • 63 : Forward slash ‘/’

Additional symbo ‘=’ is used for padding in case of bits are not in multiple of 6.

According to encoding table, 011000 010110 001001 100011 which is 24 (011000) 22 (010110) 9 (001001) 35 (100011) converted to YWJj in Base64 format.

public class Encoder {

    private static final Encoder ENCODER = new Encoder();

    private static final int ENCODE_BASE = 6;

    private final char[] encodingTable = new char[64];

    private Encoder() {
        initEncodingTable();
    }

    public static Encoder getInstance() {
        return ENCODER;
    }

    public String encode(String input) {
        int[] binary = toBinary(input);
        StringBuilder encoded = new StringBuilder();

        for (int start = 0; start < binary.length; start += ENCODE_BASE) {
            int end = Math.min(start + ENCODE_BASE, binary.length - 1);

            int power = ENCODE_BASE - 1;
            int charIndex = 0;
            for (int j = start; j < end; j++) {
                charIndex += (binary[j] * Math.pow(2, power--));
            }

            encoded.append(encodingTable[charIndex]);
        }

        int pad = binary.length % ENCODE_BASE;
        encoded.append("=".repeat(pad));
        return encoded.toString();
    }

    private int[] toBinary(String input) {
        final int bits = 8;
        int[] binary = new int[input.length() * bits];

        try {
            byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
            int index = 0;
            int max = (int) Math.pow(2, (bits - 1));

            for (byte b : bytes) {
                int val = b;
                for (int i = 0; i < bits; i++) {
                    binary[index++] = ((val & max) == 0 ? 0 : 1);
                    val <<= 1;
                }
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        return binary;
    }

    private void initEncodingTable() {
        int index = 0;

        for (char i = 'A'; i <= 'Z'; i++)
            encodingTable[index++] = i;

        for (char i = 'a'; i <= 'z'; i++)
            encodingTable[index++] = i;

        for (char i = '0'; i <= '9'; i++)
            encodingTable[index++] = i;

        encodingTable[62] = '+';
        encodingTable[63] = '/';
    }
}

This program is implemented to make it more easier to understand. However, for optimal approach we can look into java.util.Base64 class which is bit complex to understand and considers much bigger character set and scenarios than the implemented one.