1d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker/* 2d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Copyright (C) 2010 The Android Open Source Project 3d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 4d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Licensed under the Apache License, Version 2.0 (the "License"); 5d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * you may not use this file except in compliance with the License. 6d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * You may obtain a copy of the License at 7d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 8d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * http://www.apache.org/licenses/LICENSE-2.0 9d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 10d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Unless required by applicable law or agreed to in writing, software 11d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * distributed under the License is distributed on an "AS IS" BASIS, 12d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * See the License for the specific language governing permissions and 14d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * limitations under the License. 15d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 16d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 17ab69e29c1927bdc6143324eba5ccd78f7c43128dDoug Zongkerpackage android.util; 18d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 199df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongkerimport java.io.UnsupportedEncodingException; 209df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 21d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker/** 229df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Utilities for encoding and decoding the Base64 representation of 239df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * binary data. See RFCs <a 249df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a 259df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>. 26d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 27d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongkerpublic class Base64 { 28d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 29d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Default values for encoder/decoder flags. 30d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 31d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static final int DEFAULT = 0; 32d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 33d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 349df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Encoder flag bit to omit the padding '=' characters at the end 359df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * of the output (if any). 36d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 37d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static final int NO_PADDING = 1; 38d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 39d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 409df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Encoder flag bit to omit all line terminators (i.e., the output 419df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * will be on one long line). 42d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 43d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static final int NO_WRAP = 2; 44d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 45d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 469df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Encoder flag bit to indicate lines should be terminated with a 479df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * CRLF pair instead of just an LF. Has no effect if {@code 489df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * NO_WRAP} is specified as well. 49d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 50d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static final int CRLF = 4; 51d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 52d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 539df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Encoder/decoder flag bit to indicate using the "URL and 549df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * filename safe" variant of Base64 (see RFC 3548 section 4) where 559df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * {@code -} and {@code _} are used in place of {@code +} and 569df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * {@code /}. 57d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 589df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public static final int URL_SAFE = 8; 59d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 60d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 619df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Flag to pass to {@link Base64OutputStream} to indicate that it 629df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * should not close the output stream it is wrapping when it 639df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * itself is closed. 64d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 65d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static final int NO_CLOSE = 16; 66d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 67d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // -------------------------------------------------------- 689df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // shared code 69d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // -------------------------------------------------------- 70d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 719df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /* package */ static abstract class Coder { 729df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public byte[] output; 739df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public int op; 74d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 759df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 769df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Encode/decode another block of input data. this.output is 779df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * provided by the caller, and must be big enough to hold all 789df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * the coded data. On exit, this.opwill be set to the length 799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * of the coded data. 809df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * 819df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * @param finish true if this is the final call to process for 829df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * this object. Will finalize the coder state and 839df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * include any final bytes in the output. 849df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * 859df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * @return true if the input so far is good; false if some 869df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * error has been detected in the input stream.. 879df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 889df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public abstract boolean process(byte[] input, int offset, int len, boolean finish); 899df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 909df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 919df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * @return the maximum number of bytes a call to process() 929df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * could produce for the given number of input bytes. This may 939df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * be an overestimate. 949df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 959df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public abstract int maxOutputSize(int len); 969df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 979df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 989df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // -------------------------------------------------------- 999df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // decoding 1009df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // -------------------------------------------------------- 101d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 102d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 103d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Decode the Base64-encoded data in input and return the data in 104d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * a new byte array. 105d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 1069df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * <p>The padding '=' characters at the end are considered optional, but 107d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * if any are present, there must be the correct number of them. 108d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 1098fe5571aa46321a97dd6e7470e9df208a0324103Doug Zongker * @param str the input String to decode, which is converted to 110d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * bytes using the default charset 111d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the decoded output. 112d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Pass {@code DEFAULT} to decode standard Base64. 113d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 114d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @throws IllegalArgumentException if the input contains 115d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * incorrect padding 116d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 117d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static byte[] decode(String str, int flags) { 118d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return decode(str.getBytes(), flags); 119d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 120d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 121d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 122d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Decode the Base64-encoded data in input and return the data in 123d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * a new byte array. 124d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 1259df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * <p>The padding '=' characters at the end are considered optional, but 126d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * if any are present, there must be the correct number of them. 127d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 128d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param input the input array to decode 129d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the decoded output. 130d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Pass {@code DEFAULT} to decode standard Base64. 131d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 132d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @throws IllegalArgumentException if the input contains 133d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * incorrect padding 134d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 135d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static byte[] decode(byte[] input, int flags) { 136d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return decode(input, 0, input.length, flags); 137d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 138d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 139d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 140d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Decode the Base64-encoded data in input and return the data in 141d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * a new byte array. 142d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 1439df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * <p>The padding '=' characters at the end are considered optional, but 144d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * if any are present, there must be the correct number of them. 145d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 146d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param input the data to decode 147d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param offset the position within the input array at which to start 148d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param len the number of bytes of input to decode 149d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the decoded output. 150d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Pass {@code DEFAULT} to decode standard Base64. 151d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 152d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @throws IllegalArgumentException if the input contains 153d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * incorrect padding 154d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 155d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static byte[] decode(byte[] input, int offset, int len, int flags) { 156d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Allocate space for the most data the input could represent. 157d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // (It could contain less if it contains whitespace, etc.) 1589df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker Decoder decoder = new Decoder(flags, new byte[len*3/4]); 159d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 1609df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (!decoder.process(input, offset, len, true)) { 161d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker throw new IllegalArgumentException("bad base-64"); 162d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 163d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 164d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Maybe we got lucky and allocated exactly enough output space. 1659df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (decoder.op == decoder.output.length) { 1669df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return decoder.output; 167d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 168d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 169d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Need to shorten the array, so allocate a new one of the 170d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // right size and copy. 1719df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker byte[] temp = new byte[decoder.op]; 1729df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker System.arraycopy(decoder.output, 0, temp, 0, decoder.op); 173d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return temp; 174d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 175d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 1769df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /* package */ static class Decoder extends Coder { 1779df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 1789df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Lookup table for turning bytes into their position in the 1799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Base64 alphabet. 1809df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 1819df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private static final int DECODE[] = { 1829df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1839df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1849df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 1859df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, 1869df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1879df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 1889df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 1899df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 1909df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1919df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1929df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1939df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1949df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1959df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1969df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1979df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1989df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker }; 1999df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2009df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 2019df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Decode lookup table for the "web safe" variant (RFC 3548 2029df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * sec. 4) where - and _ replace + and /. 2039df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 2049df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private static final int DECODE_WEBSAFE[] = { 2059df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2069df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2079df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 2089df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, 2099df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 2109df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, 2119df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 2129df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 2139df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2149df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2159df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2169df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2179df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2189df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2199df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2209df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2219df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker }; 2229df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2239df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** Non-data values in the DECODE arrays. */ 2249df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private static final int SKIP = -1; 2259df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private static final int EQUALS = -2; 2269df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2279df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 2289df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * States 0-3 are reading through the next input tuple. 2299df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * State 4 is having read one '=' and expecting exactly 2309df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * one more. 2319df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * State 5 is expecting no more data or padding characters 2329df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * in the input. 2339df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * State 6 is the error state; an error has been detected 2349df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * in the input and no future input can "fix" it. 2359df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 2369df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private int state; // state number (0 to 6) 2379df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private int value; 2389df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2399df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final private int[] alphabet; 2409df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2419df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public Decoder(int flags, byte[] output) { 242d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker this.output = output; 243d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 2449df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; 245d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker state = 0; 246d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker value = 0; 247d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 248d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 2499df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 2509df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * @return an overestimate for the number of bytes {@code 2519df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * len} bytes could decode to. 2529df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 2539df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public int maxOutputSize(int len) { 2549df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return len * 3/4 + 10; 2559df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 256d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 2579df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 2589df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Decode another block of input data. 2599df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * 2609df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * @return true if the state machine is still healthy. false if 2619df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * bad base-64 data has been detected in the input stream. 2629df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 2639df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public boolean process(byte[] input, int offset, int len, boolean finish) { 2649df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (this.state == 6) return false; 2659df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2669df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int p = offset; 2679df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker len += offset; 2689df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2699df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // Using local variables makes the decoder about 12% 2709df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // faster than if we manipulate the member variables in 2719df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // the loop. (Even alphabet makes a measurable 2729df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // difference, which is somewhat surprising to me since 2739df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // the member variable is final.) 2749df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int state = this.state; 2759df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int value = this.value; 2769df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int op = 0; 2779df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final byte[] output = this.output; 2789df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final int[] alphabet = this.alphabet; 2799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 2809df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker while (p < len) { 2819df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // Try the fast path: we're starting a new tuple and the 2829df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // next four bytes of the input stream are all data 2839df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // bytes. This corresponds to going through states 2849df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // 0-1-2-3-0. We expect to use this method for most of 2859df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // the data. 2869df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // 2879df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // If any of the next four bytes of input are non-data 2889df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // (whitespace, etc.), value will end up negative. (All 2899df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // the non-data values in decode are small negative 2909df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // numbers, so shifting any of them up and or'ing them 2919df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // together will result in a value with its top bit set.) 2929df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // 2939df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // You can remove this whole block and the output should 2949df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // be the same, just slower. 2959df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (state == 0) { 2969df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker while (p+4 <= len && 2979df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (value = ((alphabet[input[p] & 0xff] << 18) | 2989df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (alphabet[input[p+1] & 0xff] << 12) | 2999df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (alphabet[input[p+2] & 0xff] << 6) | 3009df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (alphabet[input[p+3] & 0xff]))) >= 0) { 3019df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op+2] = (byte) value; 3029df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op+1] = (byte) (value >> 8); 3039df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op] = (byte) (value >> 16); 3049df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker op += 3; 3059df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker p += 4; 3069df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 3079df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (p >= len) break; 3089df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 309d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 3109df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // The fast path isn't available -- either we've read a 3119df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // partial tuple, or the next four input bytes aren't all 3129df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // data, or whatever. Fall back to the slower state 3139df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // machine implementation. 314d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 3159df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int d = alphabet[input[p++] & 0xff]; 3169df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 3179df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker switch (state) { 318d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 0: 319d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (d >= 0) { 320d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker value = d; 321d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker ++state; 322d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d != SKIP) { 3239df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 324d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 325d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 326d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 327d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 328d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 1: 329d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (d >= 0) { 330d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker value = (value << 6) | d; 331d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker ++state; 332d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d != SKIP) { 3339df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 334d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 335d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 336d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 337d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 338d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 2: 339d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (d >= 0) { 340d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker value = (value << 6) | d; 341d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker ++state; 342d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d == EQUALS) { 343d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Emit the last (partial) output tuple; 344d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // expect exactly one more padding character. 345d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op++] = (byte) (value >> 4); 346d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker state = 4; 347d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d != SKIP) { 3489df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 349d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 350d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 351d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 352d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 353d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 3: 354d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (d >= 0) { 355d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Emit the output triple and return to state 0. 356d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker value = (value << 6) | d; 357d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op+2] = (byte) value; 358d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op+1] = (byte) (value >> 8); 359d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op] = (byte) (value >> 16); 360d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker op += 3; 361d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker state = 0; 362d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d == EQUALS) { 363d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Emit the last (partial) output tuple; 364d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // expect no further data or padding characters. 365d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op+1] = (byte) (value >> 2); 366d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op] = (byte) (value >> 10); 367d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker op += 2; 368d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker state = 5; 369d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d != SKIP) { 3709df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 371d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 372d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 373d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 374d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 375d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 4: 376d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (d == EQUALS) { 377d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker ++state; 378d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else if (d != SKIP) { 3799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 380d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 381d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 382d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 383d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 384d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 5: 385d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (d != SKIP) { 3869df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 387d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 388d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 389d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 3909df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 391d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 392d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 3939df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (!finish) { 3949df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // We're out of input, but a future call could provide 3959df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // more. 3969df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = state; 3979df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.value = value; 3989df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.op = op; 3999df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return true; 4009df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 401d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 4029df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // Done reading input. Now figure out where we are left in 4039df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // the state machine and finish up. 404d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 4059df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker switch (state) { 406d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 0: 407d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Output length is a multiple of three. Fine. 408d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 409d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 1: 410d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Read one extra input byte, which isn't enough to 411d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // make another output byte. Illegal. 4129df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 413d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 414d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 2: 415d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Read two extra input bytes, enough to emit 1 more 416d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // output byte. Fine. 417d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op++] = (byte) (value >> 4); 418d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 419d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 3: 420d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Read three extra input bytes, enough to emit 2 more 421d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // output bytes. Fine. 4229df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = (byte) (value >> 10); 4239df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = (byte) (value >> 2); 424d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 425d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 4: 426d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Read one padding '=' when we expected 2. Illegal. 4279df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = 6; 428d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return false; 429d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 5: 430d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Read all the padding '='s we expected and no more. 431d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Fine. 432d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker break; 4339df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 434d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 4359df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.state = state; 4369df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.op = op; 4379df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return true; 4389df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 439d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 440d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 441d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // -------------------------------------------------------- 442d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // encoding 443d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // -------------------------------------------------------- 444d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 445d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 446d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Base64-encode the given data and return a newly allocated 447d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * String with the result. 448d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 449d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param input the data to encode 450d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the encoded output. 451d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Passing {@code DEFAULT} results in output that 452d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * adheres to RFC 2045. 453d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 454d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static String encodeToString(byte[] input, int flags) { 4559df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker try { 4569df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return new String(encode(input, flags), "US-ASCII"); 4579df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } catch (UnsupportedEncodingException e) { 4589df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // US-ASCII is guaranteed to be available. 4599df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker throw new AssertionError(e); 4609df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 461d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 462d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 463d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 464d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Base64-encode the given data and return a newly allocated 465d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * String with the result. 466d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 467d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param input the data to encode 468d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param offset the position within the input array at which to 469d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * start 470d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param len the number of bytes of input to encode 471d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the encoded output. 472d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Passing {@code DEFAULT} results in output that 473d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * adheres to RFC 2045. 474d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 475d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static String encodeToString(byte[] input, int offset, int len, int flags) { 4769df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker try { 4779df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return new String(encode(input, offset, len, flags), "US-ASCII"); 4789df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } catch (UnsupportedEncodingException e) { 4799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // US-ASCII is guaranteed to be available. 4809df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker throw new AssertionError(e); 4819df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 482d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 483d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 484d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 485d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Base64-encode the given data and return a newly allocated 486d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * byte[] with the result. 487d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 488d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param input the data to encode 489d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the encoded output. 490d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Passing {@code DEFAULT} results in output that 491d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * adheres to RFC 2045. 492d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 493d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static byte[] encode(byte[] input, int flags) { 494d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker return encode(input, 0, input.length, flags); 495d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 496d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 497d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker /** 498d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Base64-encode the given data and return a newly allocated 499d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * byte[] with the result. 500d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * 501d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param input the data to encode 502d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param offset the position within the input array at which to 503d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * start 504d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param len the number of bytes of input to encode 505d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * @param flags controls certain features of the encoded output. 506d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * Passing {@code DEFAULT} results in output that 507d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker * adheres to RFC 2045. 508d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */ 509d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker public static byte[] encode(byte[] input, int offset, int len, int flags) { 5109df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker Encoder encoder = new Encoder(flags, null); 511d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 512d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Compute the exact length of the array we will produce. 513d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker int output_len = len / 3 * 4; 514d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 515d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Account for the tail of the data and the padding bytes, if any. 5169df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (encoder.do_padding) { 517d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (len % 3 > 0) { 518d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output_len += 4; 519d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 520d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } else { 521d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker switch (len % 3) { 522d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 0: break; 523d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 1: output_len += 2; break; 524d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker case 2: output_len += 3; break; 525d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 526d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 527d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 528d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker // Account for the newlines, if any. 5299df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (encoder.do_newline && len > 0) { 5309df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * 5319df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (encoder.do_cr ? 2 : 1); 532d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 533d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 5349df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker encoder.output = new byte[output_len]; 5359df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker encoder.process(input, offset, len, true); 536d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 5379df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker assert encoder.op == output_len; 538d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 5399df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return encoder.output; 540d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 541d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 5429df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /* package */ static class Encoder extends Coder { 5439df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 5449df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Emit a new line every this many output tuples. Corresponds to 5459df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * a 76-character line length (the maximum allowable according to 5469df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>). 5479df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 5489df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public static final int LINE_GROUPS = 19; 5499df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 5509df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 5519df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Lookup table for turning Base64 alphabet positions (6 bits) 5529df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * into output bytes. 5539df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 5549df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private static final byte ENCODE[] = { 5559df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 5569df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 5579df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 5589df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 5599df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker }; 5609df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 5619df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 5629df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * Lookup table for turning Base64 alphabet positions (6 bits) 5639df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * into output bytes. 5649df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 5659df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private static final byte ENCODE_WEBSAFE[] = { 5669df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 5679df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 5689df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 5699df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 5709df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker }; 5719df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 5729df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final private byte[] tail; 5739df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /* package */ int tailLen; 5749df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker private int count; 575d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 576d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker final public boolean do_padding; 577d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker final public boolean do_newline; 578d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker final public boolean do_cr; 5799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final private byte[] alphabet; 580d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 5819df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public Encoder(int flags, byte[] output) { 582d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker this.output = output; 583d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 584d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker do_padding = (flags & NO_PADDING) == 0; 585d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker do_newline = (flags & NO_WRAP) == 0; 586d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker do_cr = (flags & CRLF) != 0; 5879df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; 588d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 589d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker tail = new byte[2]; 590d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker tailLen = 0; 591d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 592d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker count = do_newline ? LINE_GROUPS : -1; 593d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 594d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 5959df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker /** 5969df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * @return an overestimate for the number of bytes {@code 5979df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker * len} bytes could encode to. 5989df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker */ 5999df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public int maxOutputSize(int len) { 6009df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return len * 8/5 + 10; 6019df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 602d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6039df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker public boolean process(byte[] input, int offset, int len, boolean finish) { 6049df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // Using local variables makes the encoder about 9% faster. 6059df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final byte[] alphabet = this.alphabet; 6069df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker final byte[] output = this.output; 6079df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int op = 0; 6089df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int count = this.count; 609d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6109df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int p = offset; 6119df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker len += offset; 6129df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int v = -1; 613d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6149df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // First we need to concatenate the tail of the previous call 6159df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // with any input bytes available now and see if we can empty 6169df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // the tail. 617d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6189df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker switch (tailLen) { 6199df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker case 0: 6209df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // There was no tail. 6219df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker break; 622d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6239df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker case 1: 6249df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (p+2 <= len) { 6259df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // A 1-byte tail with at least 2 bytes of 6269df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // input available now. 6279df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker v = ((tail[0] & 0xff) << 16) | 6289df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker ((input[p++] & 0xff) << 8) | 6299df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (input[p++] & 0xff); 6309df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tailLen = 0; 6319df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker }; 6329df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker break; 633d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6349df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker case 2: 6359df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (p+1 <= len) { 6369df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // A 2-byte tail with at least 1 byte of input. 6379df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker v = ((tail[0] & 0xff) << 16) | 6389df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker ((tail[1] & 0xff) << 8) | 6399df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (input[p++] & 0xff); 6409df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tailLen = 0; 6419df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 6429df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker break; 643d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 644d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6459df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (v != -1) { 6469df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[(v >> 18) & 0x3f]; 6479df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[(v >> 12) & 0x3f]; 6489df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[(v >> 6) & 0x3f]; 6499df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[v & 0x3f]; 6509df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (--count == 0) { 6519df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_cr) output[op++] = '\r'; 6529df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = '\n'; 6539df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker count = LINE_GROUPS; 6549df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 655d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 656d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 6579df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // At this point either there is no tail, or there are fewer 6589df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // than 3 bytes of input available. 6599df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 6609df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // The main loop, turning 3 input bytes into 4 output bytes on 6619df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // each iteration. 6629df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker while (p+3 <= len) { 6639df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker v = ((input[p] & 0xff) << 16) | 6649df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker ((input[p+1] & 0xff) << 8) | 6659df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (input[p+2] & 0xff); 6669df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op] = alphabet[(v >> 18) & 0x3f]; 6679df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op+1] = alphabet[(v >> 12) & 0x3f]; 6689df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op+2] = alphabet[(v >> 6) & 0x3f]; 6699df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op+3] = alphabet[v & 0x3f]; 6709df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker p += 3; 6719df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker op += 4; 6729df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (--count == 0) { 673d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (do_cr) output[op++] = '\r'; 674d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op++] = '\n'; 6759df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker count = LINE_GROUPS; 676d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 6779df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 6789df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 6799df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (finish) { 6809df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // Finish up the tail of the input. Note that we need to 6819df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // consume any bytes in tail before any bytes 6829df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // remaining in input; there should be at most two bytes 6839df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // total. 6849df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 6859df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (p-tailLen == len-1) { 6869df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int t = 0; 6879df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; 6889df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tailLen -= t; 6899df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[(v >> 6) & 0x3f]; 6909df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[v & 0x3f]; 6919df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_padding) { 6929df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = '='; 6939df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = '='; 6949df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 6959df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_newline) { 6969df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_cr) output[op++] = '\r'; 6979df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = '\n'; 6989df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 6999df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } else if (p-tailLen == len-2) { 7009df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker int t = 0; 7019df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | 7029df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); 7039df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tailLen -= t; 7049df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[(v >> 12) & 0x3f]; 7059df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[(v >> 6) & 0x3f]; 7069df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = alphabet[v & 0x3f]; 7079df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_padding) { 7089df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = '='; 7099df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 7109df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_newline) { 7119df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (do_cr) output[op++] = '\r'; 7129df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker output[op++] = '\n'; 7139df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 7149df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } else if (do_newline && op > 0 && count != LINE_GROUPS) { 715d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker if (do_cr) output[op++] = '\r'; 716d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker output[op++] = '\n'; 717d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 718d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 7199df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker assert tailLen == 0; 7209df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker assert p == len; 7219df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } else { 7229df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // Save the leftovers in tail to be consumed on the next 7239df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker // call to encodeInternal. 7249df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 7259df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker if (p == len-1) { 7269df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tail[tailLen++] = input[p]; 7279df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } else if (p == len-2) { 7289df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tail[tailLen++] = input[p]; 7299df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker tail[tailLen++] = input[p+1]; 7309df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 731d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 732d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 7339df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.op = op; 7349df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker this.count = count; 7359df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker 7369df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker return true; 7379df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker } 738d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker } 739d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker 740d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker private Base64() { } // don't instantiate 741d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker} 742