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