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