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* @version $Revision$
21*/
22
23package org.apache.harmony.luni.util;
24
25import java.io.UnsupportedEncodingException;
26
27/**
28 * This class implements Base64 encoding/decoding functionality
29 * as specified in RFC 2045 (http://www.ietf.org/rfc/rfc2045.txt).
30 */
31public class Base64 {
32
33    public static byte[] decode(byte[] in) {
34        return decode(in, in.length);
35    }
36
37    public static byte[] decode(byte[] in, int len) {
38        // approximate output length
39        int length = len / 4 * 3;
40        // return an empty array on emtpy or short input without padding
41        if (length == 0) {
42            return new byte[0];
43        }
44        // temporary array
45        byte[] out = new byte[length];
46        // number of padding characters ('=')
47        int pad = 0;
48        byte chr;
49        // compute the number of the padding characters
50        // and adjust the length of the input
51        for (;;len--) {
52            chr = in[len-1];
53            // skip the neutral characters
54            if ((chr == '\n') || (chr == '\r') ||
55                    (chr == ' ') || (chr == '\t')) {
56                continue;
57            }
58            if (chr == '=') {
59                pad++;
60            } else {
61                break;
62            }
63        }
64        // index in the output array
65        int out_index = 0;
66        // index in the input array
67        int in_index = 0;
68        // holds the value of the input character
69        int bits = 0;
70        // holds the value of the input quantum
71        int quantum = 0;
72        for (int i=0; i<len; i++) {
73            chr = in[i];
74            // skip the neutral characters
75            if ((chr == '\n') || (chr == '\r') ||
76                    (chr == ' ') || (chr == '\t')) {
77                continue;
78            }
79            if ((chr >= 'A') && (chr <= 'Z')) {
80                // char ASCII value
81                //  A    65    0
82                //  Z    90    25 (ASCII - 65)
83                bits = chr - 65;
84            } else if ((chr >= 'a') && (chr <= 'z')) {
85                // char ASCII value
86                //  a    97    26
87                //  z    122   51 (ASCII - 71)
88                bits = chr - 71;
89            } else if ((chr >= '0') && (chr <= '9')) {
90                // char ASCII value
91                //  0    48    52
92                //  9    57    61 (ASCII + 4)
93                bits = chr + 4;
94            } else if (chr == '+') {
95                bits = 62;
96            } else if (chr == '/') {
97                bits = 63;
98            } else {
99                return null;
100            }
101            // append the value to the quantum
102            quantum = (quantum << 6) | (byte) bits;
103            if (in_index%4 == 3) {
104                // 4 characters were read, so make the output:
105                out[out_index++] = (byte) ((quantum & 0x00FF0000) >> 16);
106                out[out_index++] = (byte) ((quantum & 0x0000FF00) >> 8);
107                out[out_index++] = (byte) (quantum & 0x000000FF);
108            }
109            in_index++;
110        }
111        if (pad > 0) {
112            // adjust the quantum value according to the padding
113            quantum = quantum << (6*pad);
114            // make output
115            out[out_index++] = (byte) ((quantum & 0x00FF0000) >> 16);
116            if (pad == 1) {
117                out[out_index++] = (byte) ((quantum & 0x0000FF00) >> 8);
118            }
119        }
120        // create the resulting array
121        byte[] result = new byte[out_index];
122        System.arraycopy(out, 0, result, 0, out_index);
123        return result;
124    }
125
126    private static final byte[] map = new byte[]
127        {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
128         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
129         'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
130         'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
131         '4', '5', '6', '7', '8', '9', '+', '/'};
132
133    public static String encode(byte[] in, String charsetName) throws UnsupportedEncodingException {
134        int length = in.length * 4 / 3;
135        length += length / 76 + 3; // for crlr
136        byte[] out = new byte[length];
137        int index = 0, i, crlr = 0, end = in.length - in.length%3;
138        for (i=0; i<end; i+=3) {
139            out[index++] = map[(in[i] & 0xff) >> 2];
140            out[index++] = map[((in[i] & 0x03) << 4)
141                                | ((in[i+1] & 0xff) >> 4)];
142            out[index++] = map[((in[i+1] & 0x0f) << 2)
143                                | ((in[i+2] & 0xff) >> 6)];
144            out[index++] = map[(in[i+2] & 0x3f)];
145            if (((index - crlr)%76 == 0) && (index != 0)) {
146                out[index++] = '\n';
147                crlr++;
148                //out[index++] = '\r';
149                //crlr++;
150            }
151        }
152        switch (in.length % 3) {
153            case 1:
154                out[index++] = map[(in[end] & 0xff) >> 2];
155                out[index++] = map[(in[end] & 0x03) << 4];
156                out[index++] = '=';
157                out[index++] = '=';
158                break;
159            case 2:
160                out[index++] = map[(in[end] & 0xff) >> 2];
161                out[index++] = map[((in[end] & 0x03) << 4)
162                                    | ((in[end+1] & 0xff) >> 4)];
163                out[index++] = map[((in[end+1] & 0x0f) << 2)];
164                out[index++] = '=';
165                break;
166        }
167        return new String(out, 0, index, charsetName);
168    }
169}
170
171