1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Alexander Y. Kleymenov
20*/
21
22package org.apache.harmony.luni.util;
23
24import java.io.UnsupportedEncodingException;
25
26/**
27 * This class implements Base64 encoding/decoding functionality
28 * as specified in RFC 2045 (http://www.ietf.org/rfc/rfc2045.txt).
29 */
30public class Base64 {
31
32    public static byte[] decode(byte[] in) {
33        return decode(in, in.length);
34    }
35
36    public static byte[] decode(byte[] in, int len) {
37        // approximate output length
38        int length = len / 4 * 3;
39        // return an empty array on emtpy or short input without padding
40        if (length == 0) {
41            return new byte[0];
42        }
43        // temporary array
44        byte[] out = new byte[length];
45        // number of padding characters ('=')
46        int pad = 0;
47        byte chr;
48        // compute the number of the padding characters
49        // and adjust the length of the input
50        for (;;len--) {
51            chr = in[len-1];
52            // skip the neutral characters
53            if ((chr == '\n') || (chr == '\r') ||
54                    (chr == ' ') || (chr == '\t')) {
55                continue;
56            }
57            if (chr == '=') {
58                pad++;
59            } else {
60                break;
61            }
62        }
63        // index in the output array
64        int out_index = 0;
65        // index in the input array
66        int in_index = 0;
67        // holds the value of the input character
68        int bits = 0;
69        // holds the value of the input quantum
70        int quantum = 0;
71        for (int i=0; i<len; i++) {
72            chr = in[i];
73            // skip the neutral characters
74            if ((chr == '\n') || (chr == '\r') ||
75                    (chr == ' ') || (chr == '\t')) {
76                continue;
77            }
78            if ((chr >= 'A') && (chr <= 'Z')) {
79                // char ASCII value
80                //  A    65    0
81                //  Z    90    25 (ASCII - 65)
82                bits = chr - 65;
83            } else if ((chr >= 'a') && (chr <= 'z')) {
84                // char ASCII value
85                //  a    97    26
86                //  z    122   51 (ASCII - 71)
87                bits = chr - 71;
88            } else if ((chr >= '0') && (chr <= '9')) {
89                // char ASCII value
90                //  0    48    52
91                //  9    57    61 (ASCII + 4)
92                bits = chr + 4;
93            } else if (chr == '+') {
94                bits = 62;
95            } else if (chr == '/') {
96                bits = 63;
97            } else {
98                return null;
99            }
100            // append the value to the quantum
101            quantum = (quantum << 6) | (byte) bits;
102            if (in_index%4 == 3) {
103                // 4 characters were read, so make the output:
104                out[out_index++] = (byte) ((quantum & 0x00FF0000) >> 16);
105                out[out_index++] = (byte) ((quantum & 0x0000FF00) >> 8);
106                out[out_index++] = (byte) (quantum & 0x000000FF);
107            }
108            in_index++;
109        }
110        if (pad > 0) {
111            // adjust the quantum value according to the padding
112            quantum = quantum << (6*pad);
113            // make output
114            out[out_index++] = (byte) ((quantum & 0x00FF0000) >> 16);
115            if (pad == 1) {
116                out[out_index++] = (byte) ((quantum & 0x0000FF00) >> 8);
117            }
118        }
119        // create the resulting array
120        byte[] result = new byte[out_index];
121        System.arraycopy(out, 0, result, 0, out_index);
122        return result;
123    }
124
125    private static final byte[] map = new byte[]
126        {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
127         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
128         'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
129         'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
130         '4', '5', '6', '7', '8', '9', '+', '/'};
131
132    public static String encode(byte[] in, String charsetName) throws UnsupportedEncodingException {
133        int length = in.length * 4 / 3;
134        length += length / 76 + 3; // for crlr
135        byte[] out = new byte[length];
136        int index = 0, i, crlr = 0, end = in.length - in.length%3;
137        for (i=0; i<end; i+=3) {
138            out[index++] = map[(in[i] & 0xff) >> 2];
139            out[index++] = map[((in[i] & 0x03) << 4)
140                                | ((in[i+1] & 0xff) >> 4)];
141            out[index++] = map[((in[i+1] & 0x0f) << 2)
142                                | ((in[i+2] & 0xff) >> 6)];
143            out[index++] = map[(in[i+2] & 0x3f)];
144            if (((index - crlr)%76 == 0) && (index != 0)) {
145                out[index++] = '\n';
146                crlr++;
147                //out[index++] = '\r';
148                //crlr++;
149            }
150        }
151        switch (in.length % 3) {
152            case 1:
153                out[index++] = map[(in[end] & 0xff) >> 2];
154                out[index++] = map[(in[end] & 0x03) << 4];
155                out[index++] = '=';
156                out[index++] = '=';
157                break;
158            case 2:
159                out[index++] = map[(in[end] & 0xff) >> 2];
160                out[index++] = map[((in[end] & 0x03) << 4)
161                                    | ((in[end+1] & 0xff) >> 4)];
162                out[index++] = map[((in[end+1] & 0x0f) << 2)];
163                out[index++] = '=';
164                break;
165        }
166        return new String(out, 0, index, charsetName);
167    }
168}
169
170