1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Licensed to the Apache Software Foundation (ASF) under one or more
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  contributor license agreements.  See the NOTICE file distributed with
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  this work for additional information regarding copyright ownership.
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  The ASF licenses this file to You under the Apache License, Version 2.0
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  (the "License"); you may not use this file except in compliance with
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  the License.  You may obtain a copy of the License at
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     http://www.apache.org/licenses/LICENSE-2.0
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Unless required by applicable law or agreed to in writing, software
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  distributed under the License is distributed on an "AS IS" BASIS,
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  See the License for the specific language governing permissions and
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  limitations under the License.
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
1954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @author Alexander Y. Kleymenov
2054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */
21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage okio;
23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.UnsupportedEncodingException;
25c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerfinal class Base64 {
2754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private Base64() {
2854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static byte[] decode(String in) {
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Ignore trailing '=' padding and whitespace from the input.
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int limit = in.length();
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (; limit > 0; limit--) {
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      char c = in.charAt(limit - 1);
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') {
3654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
3754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
3854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If the input includes whitespace, this output array will be longer than necessary.
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] out = new byte[(int) (limit * 6L / 8L)];
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int outCount = 0;
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int inCount = 0;
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int word = 0;
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int pos = 0; pos < limit; pos++) {
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      char c = in.charAt(pos);
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      int bits;
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (c >= 'A' && c <= 'Z') {
5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // char ASCII value
5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        //  A    65    0
5354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        //  Z    90    25 (ASCII - 65)
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        bits = c - 65;
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (c >= 'a' && c <= 'z') {
5654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // char ASCII value
5754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        //  a    97    26
5854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        //  z    122   51 (ASCII - 71)
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        bits = c - 71;
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (c >= '0' && c <= '9') {
6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // char ASCII value
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        //  0    48    52
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        //  9    57    61 (ASCII + 4)
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        bits = c + 4;
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (c == '+') {
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        bits = 62;
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (c == '/') {
6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        bits = 63;
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        continue;
7154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
7254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
7354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Append this char's 6 bits to the word.
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      word = (word << 6) | (byte) bits;
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes.
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      inCount++;
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (inCount % 4 == 0) {
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        out[outCount++] = (byte) (word >> 16);
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        out[outCount++] = (byte) (word >> 8);
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        out[outCount++] = (byte) word;
8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int lastWordChars = inCount % 4;
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (lastWordChars == 1) {
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // We read 1 char followed by "===". But 6 bits is a truncated byte! Fail.
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else if (lastWordChars == 2) {
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits.
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      word = word << 12;
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      out[outCount++] = (byte) (word >> 16);
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else if (lastWordChars == 3) {
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits.
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      word = word << 6;
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      out[outCount++] = (byte) (word >> 16);
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      out[outCount++] = (byte) (word >> 8);
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If we sized our out array perfectly, we're done.
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (outCount == out.length) return out;
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Copy the decoded bytes to a new, right-sized array.
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] prefix = new byte[outCount];
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    System.arraycopy(out, 0, prefix, 0, outCount);
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return prefix;
10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
110c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final byte[] MAP = new byte[] {
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
11554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      '5', '6', '7', '8', '9', '+', '/'
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  };
117c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static String encode(byte[] in) {
11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int length = (in.length + 2) * 4 / 3;
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    byte[] out = new byte[length];
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int index = 0, end = in.length - in.length % 3;
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < end; i += 3) {
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      out[index++] = MAP[(in[i] & 0xff) >> 2];
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      out[index++] = MAP[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)];
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      out[index++] = MAP[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)];
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      out[index++] = MAP[(in[i + 2] & 0x3f)];
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    switch (in.length % 3) {
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      case 1:
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = MAP[(in[end] & 0xff) >> 2];
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = MAP[(in[end] & 0x03) << 4];
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = '=';
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = '=';
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      case 2:
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = MAP[(in[end] & 0xff) >> 2];
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = MAP[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)];
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = MAP[((in[end + 1] & 0x0f) << 2)];
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out[index++] = '=';
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return new String(out, 0, index, "US-ASCII");
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnsupportedEncodingException e) {
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new AssertionError(e);
146c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
148c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
149