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