1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* @author Alexander Y. Kleymenov
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*/
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2232b2c95c350002f67c8b3e65777161feda766b72Jesse Wilsonpackage libcore.io;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
242a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughesimport java.nio.charset.StandardCharsets;
256186821cb13f4ac7ff50950c813394367e021eaeJesse Wilsonimport libcore.util.EmptyArray;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
2832b2c95c350002f67c8b3e65777161feda766b72Jesse Wilson * <a href="http://www.ietf.org/rfc/rfc2045.txt">Base64</a> encoder/decoder.
2932b2c95c350002f67c8b3e65777161feda766b72Jesse Wilson * In violation of the RFC, this encoder doesn't wrap lines at 76 columns.
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
3132b2c95c350002f67c8b3e65777161feda766b72Jesse Wilsonpublic final class Base64 {
32b1b8fbeeae579e9f49c41166bb5d9b2e60b75438Elliott Hughes    private Base64() {
33b1b8fbeeae579e9f49c41166bb5d9b2e60b75438Elliott Hughes    }
34f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static byte[] decode(byte[] in) {
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return decode(in, in.length);
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
38f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static byte[] decode(byte[] in, int len) {
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // approximate output length
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int length = len / 4 * 3;
42b1b8fbeeae579e9f49c41166bb5d9b2e60b75438Elliott Hughes        // return an empty array on empty or short input without padding
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (length == 0) {
44693eacca9fa67ad79d1b35dbaad61c5ac1ac457cElliott Hughes            return EmptyArray.BYTE;
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // temporary array
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte[] out = new byte[length];
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // number of padding characters ('=')
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int pad = 0;
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte chr;
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // compute the number of the padding characters
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // and adjust the length of the input
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (;;len--) {
54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            chr = in[len-1];
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // skip the neutral characters
56f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            if ((chr == '\n') || (chr == '\r') ||
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    (chr == ' ') || (chr == '\t')) {
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                continue;
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (chr == '=') {
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                pad++;
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // index in the output array
679229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes        int outIndex = 0;
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // index in the input array
699229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes        int inIndex = 0;
70adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // holds the value of the input character
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int bits = 0;
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // holds the value of the input quantum
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int quantum = 0;
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int i=0; i<len; i++) {
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            chr = in[i];
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // skip the neutral characters
77f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            if ((chr == '\n') || (chr == '\r') ||
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    (chr == ' ') || (chr == '\t')) {
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                continue;
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if ((chr >= 'A') && (chr <= 'Z')) {
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // char ASCII value
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                //  A    65    0
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                //  Z    90    25 (ASCII - 65)
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                bits = chr - 65;
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else if ((chr >= 'a') && (chr <= 'z')) {
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // char ASCII value
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                //  a    97    26
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                //  z    122   51 (ASCII - 71)
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                bits = chr - 71;
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else if ((chr >= '0') && (chr <= '9')) {
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // char ASCII value
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                //  0    48    52
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                //  9    57    61 (ASCII + 4)
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                bits = chr + 4;
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else if (chr == '+') {
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                bits = 62;
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else if (chr == '/') {
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                bits = 63;
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return null;
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // append the value to the quantum
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            quantum = (quantum << 6) | (byte) bits;
1059229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes            if (inIndex%4 == 3) {
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // 4 characters were read, so make the output:
1079229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes                out[outIndex++] = (byte) (quantum >> 16);
1089229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes                out[outIndex++] = (byte) (quantum >> 8);
1099229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes                out[outIndex++] = (byte) quantum;
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1119229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes            inIndex++;
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (pad > 0) {
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // adjust the quantum value according to the padding
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            quantum = quantum << (6*pad);
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // make output
1179229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes            out[outIndex++] = (byte) (quantum >> 16);
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (pad == 1) {
1199229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes                out[outIndex++] = (byte) (quantum >> 8);
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // create the resulting array
1239229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes        byte[] result = new byte[outIndex];
1249229d47c1288e25ead3a2dc27fac8a4a2ee932a3Elliott Hughes        System.arraycopy(out, 0, result, 0, outIndex);
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return result;
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final byte[] map = new byte[]
129f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
130f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
131f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
132f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project         '4', '5', '6', '7', '8', '9', '+', '/'};
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
13532b2c95c350002f67c8b3e65777161feda766b72Jesse Wilson    public static String encode(byte[] in) {
13632b2c95c350002f67c8b3e65777161feda766b72Jesse Wilson        int length = (in.length + 2) * 4 / 3;
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte[] out = new byte[length];
13832b2c95c350002f67c8b3e65777161feda766b72Jesse Wilson        int index = 0, end = in.length - in.length % 3;
13932b2c95c350002f67c8b3e65777161feda766b72Jesse Wilson        for (int i = 0; i < end; i += 3) {
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            out[index++] = map[(in[i] & 0xff) >> 2];
141b5bde2fd72189192b52e726a2d606d70c3c8a34bElliott Hughes            out[index++] = map[((in[i] & 0x03) << 4) | ((in[i+1] & 0xff) >> 4)];
142b5bde2fd72189192b52e726a2d606d70c3c8a34bElliott Hughes            out[index++] = map[((in[i+1] & 0x0f) << 2) | ((in[i+2] & 0xff) >> 6)];
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            out[index++] = map[(in[i+2] & 0x3f)];
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        switch (in.length % 3) {
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            case 1:
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                out[index++] = map[(in[end] & 0xff) >> 2];
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                out[index++] = map[(in[end] & 0x03) << 4];
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                out[index++] = '=';
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                out[index++] = '=';
151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            case 2:
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                out[index++] = map[(in[end] & 0xff) >> 2];
154b5bde2fd72189192b52e726a2d606d70c3c8a34bElliott Hughes                out[index++] = map[((in[end] & 0x03) << 4) | ((in[end+1] & 0xff) >> 4)];
155f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                out[index++] = map[((in[end+1] & 0x0f) << 2)];
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                out[index++] = '=';
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1592a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes        return new String(out, 0, index, StandardCharsets.US_ASCII);
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
162