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