Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/util/Base64.java
38829 views
/*1* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util;2627import java.io.FilterOutputStream;28import java.io.InputStream;29import java.io.IOException;30import java.io.OutputStream;31import java.nio.ByteBuffer;32import java.nio.charset.StandardCharsets;3334/**35* This class consists exclusively of static methods for obtaining36* encoders and decoders for the Base64 encoding scheme. The37* implementation of this class supports the following types of Base6438* as specified in39* <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and40* <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.41*42* <ul>43* <li><a name="basic"><b>Basic</b></a>44* <p> Uses "The Base64 Alphabet" as specified in Table 1 of45* RFC 4648 and RFC 2045 for encoding and decoding operation.46* The encoder does not add any line feed (line separator)47* character. The decoder rejects data that contains characters48* outside the base64 alphabet.</p></li>49*50* <li><a name="url"><b>URL and Filename safe</b></a>51* <p> Uses the "URL and Filename safe Base64 Alphabet" as specified52* in Table 2 of RFC 4648 for encoding and decoding. The53* encoder does not add any line feed (line separator) character.54* The decoder rejects data that contains characters outside the55* base64 alphabet.</p></li>56*57* <li><a name="mime"><b>MIME</b></a>58* <p> Uses the "The Base64 Alphabet" as specified in Table 1 of59* RFC 2045 for encoding and decoding operation. The encoded output60* must be represented in lines of no more than 76 characters each61* and uses a carriage return {@code '\r'} followed immediately by62* a linefeed {@code '\n'} as the line separator. No line separator63* is added to the end of the encoded output. All line separators64* or other characters not found in the base64 alphabet table are65* ignored in decoding operation.</p></li>66* </ul>67*68* <p> Unless otherwise noted, passing a {@code null} argument to a69* method of this class will cause a {@link java.lang.NullPointerException70* NullPointerException} to be thrown.71*72* @author Xueming Shen73* @since 1.874*/7576public class Base64 {7778private Base64() {}7980/**81* Returns a {@link Encoder} that encodes using the82* <a href="#basic">Basic</a> type base64 encoding scheme.83*84* @return A Base64 encoder.85*/86public static Encoder getEncoder() {87return Encoder.RFC4648;88}8990/**91* Returns a {@link Encoder} that encodes using the92* <a href="#url">URL and Filename safe</a> type base6493* encoding scheme.94*95* @return A Base64 encoder.96*/97public static Encoder getUrlEncoder() {98return Encoder.RFC4648_URLSAFE;99}100101/**102* Returns a {@link Encoder} that encodes using the103* <a href="#mime">MIME</a> type base64 encoding scheme.104*105* @return A Base64 encoder.106*/107public static Encoder getMimeEncoder() {108return Encoder.RFC2045;109}110111/**112* Returns a {@link Encoder} that encodes using the113* <a href="#mime">MIME</a> type base64 encoding scheme114* with specified line length and line separators.115*116* @param lineLength117* the length of each output line (rounded down to nearest multiple118* of 4). If {@code lineLength <= 0} the output will not be separated119* in lines120* @param lineSeparator121* the line separator for each output line122*123* @return A Base64 encoder.124*125* @throws IllegalArgumentException if {@code lineSeparator} includes any126* character of "The Base64 Alphabet" as specified in Table 1 of127* RFC 2045.128*/129public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) {130Objects.requireNonNull(lineSeparator);131int[] base64 = Decoder.fromBase64;132for (byte b : lineSeparator) {133if (base64[b & 0xff] != -1)134throw new IllegalArgumentException(135"Illegal base64 line separator character 0x" + Integer.toString(b, 16));136}137if (lineLength <= 0) {138return Encoder.RFC4648;139}140return new Encoder(false, lineSeparator, lineLength >> 2 << 2, true);141}142143/**144* Returns a {@link Decoder} that decodes using the145* <a href="#basic">Basic</a> type base64 encoding scheme.146*147* @return A Base64 decoder.148*/149public static Decoder getDecoder() {150return Decoder.RFC4648;151}152153/**154* Returns a {@link Decoder} that decodes using the155* <a href="#url">URL and Filename safe</a> type base64156* encoding scheme.157*158* @return A Base64 decoder.159*/160public static Decoder getUrlDecoder() {161return Decoder.RFC4648_URLSAFE;162}163164/**165* Returns a {@link Decoder} that decodes using the166* <a href="#mime">MIME</a> type base64 decoding scheme.167*168* @return A Base64 decoder.169*/170public static Decoder getMimeDecoder() {171return Decoder.RFC2045;172}173174/**175* This class implements an encoder for encoding byte data using176* the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.177*178* <p> Instances of {@link Encoder} class are safe for use by179* multiple concurrent threads.180*181* <p> Unless otherwise noted, passing a {@code null} argument to182* a method of this class will cause a183* {@link java.lang.NullPointerException NullPointerException} to184* be thrown.185*186* @see Decoder187* @since 1.8188*/189public static class Encoder {190191private final byte[] newline;192private final int linemax;193private final boolean isURL;194private final boolean doPadding;195196private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {197this.isURL = isURL;198this.newline = newline;199this.linemax = linemax;200this.doPadding = doPadding;201}202203/**204* This array is a lookup table that translates 6-bit positive integer205* index values into their "Base64 Alphabet" equivalents as specified206* in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).207*/208private static final char[] toBase64 = {209'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',210'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',211'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',212'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',213'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'214};215216/**217* It's the lookup table for "URL and Filename safe Base64" as specified218* in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and219* '_'. This table is used when BASE64_URL is specified.220*/221private static final char[] toBase64URL = {222'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',223'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',224'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',225'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',226'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'227};228229private static final int MIMELINEMAX = 76;230private static final byte[] CRLF = new byte[] {'\r', '\n'};231232static final Encoder RFC4648 = new Encoder(false, null, -1, true);233static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);234static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);235236private final int outLength(int srclen) {237int len = 0;238if (doPadding) {239len = 4 * ((srclen + 2) / 3);240} else {241int n = srclen % 3;242len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);243}244if (linemax > 0) // line separators245len += (len - 1) / linemax * newline.length;246return len;247}248249/**250* Encodes all bytes from the specified byte array into a newly-allocated251* byte array using the {@link Base64} encoding scheme. The returned byte252* array is of the length of the resulting bytes.253*254* @param src255* the byte array to encode256* @return A newly-allocated byte array containing the resulting257* encoded bytes.258*/259public byte[] encode(byte[] src) {260int len = outLength(src.length); // dst array size261byte[] dst = new byte[len];262int ret = encode0(src, 0, src.length, dst);263if (ret != dst.length)264return Arrays.copyOf(dst, ret);265return dst;266}267268/**269* Encodes all bytes from the specified byte array using the270* {@link Base64} encoding scheme, writing the resulting bytes to the271* given output byte array, starting at offset 0.272*273* <p> It is the responsibility of the invoker of this method to make274* sure the output byte array {@code dst} has enough space for encoding275* all bytes from the input byte array. No bytes will be written to the276* output byte array if the output byte array is not big enough.277*278* @param src279* the byte array to encode280* @param dst281* the output byte array282* @return The number of bytes written to the output byte array283*284* @throws IllegalArgumentException if {@code dst} does not have enough285* space for encoding all input bytes.286*/287public int encode(byte[] src, byte[] dst) {288int len = outLength(src.length); // dst array size289if (dst.length < len)290throw new IllegalArgumentException(291"Output byte array is too small for encoding all input bytes");292return encode0(src, 0, src.length, dst);293}294295/**296* Encodes the specified byte array into a String using the {@link Base64}297* encoding scheme.298*299* <p> This method first encodes all input bytes into a base64 encoded300* byte array and then constructs a new String by using the encoded byte301* array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1302* ISO-8859-1} charset.303*304* <p> In other words, an invocation of this method has exactly the same305* effect as invoking306* {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.307*308* @param src309* the byte array to encode310* @return A String containing the resulting Base64 encoded characters311*/312@SuppressWarnings("deprecation")313public String encodeToString(byte[] src) {314byte[] encoded = encode(src);315return new String(encoded, 0, 0, encoded.length);316}317318/**319* Encodes all remaining bytes from the specified byte buffer into320* a newly-allocated ByteBuffer using the {@link Base64} encoding321* scheme.322*323* Upon return, the source buffer's position will be updated to324* its limit; its limit will not have been changed. The returned325* output buffer's position will be zero and its limit will be the326* number of resulting encoded bytes.327*328* @param buffer329* the source ByteBuffer to encode330* @return A newly-allocated byte buffer containing the encoded bytes.331*/332public ByteBuffer encode(ByteBuffer buffer) {333int len = outLength(buffer.remaining());334byte[] dst = new byte[len];335int ret = 0;336if (buffer.hasArray()) {337ret = encode0(buffer.array(),338buffer.arrayOffset() + buffer.position(),339buffer.arrayOffset() + buffer.limit(),340dst);341buffer.position(buffer.limit());342} else {343byte[] src = new byte[buffer.remaining()];344buffer.get(src);345ret = encode0(src, 0, src.length, dst);346}347if (ret != dst.length)348dst = Arrays.copyOf(dst, ret);349return ByteBuffer.wrap(dst);350}351352/**353* Wraps an output stream for encoding byte data using the {@link Base64}354* encoding scheme.355*356* <p> It is recommended to promptly close the returned output stream after357* use, during which it will flush all possible leftover bytes to the underlying358* output stream. Closing the returned output stream will close the underlying359* output stream.360*361* @param os362* the output stream.363* @return the output stream for encoding the byte data into the364* specified Base64 encoded format365*/366public OutputStream wrap(OutputStream os) {367Objects.requireNonNull(os);368return new EncOutputStream(os, isURL ? toBase64URL : toBase64,369newline, linemax, doPadding);370}371372/**373* Returns an encoder instance that encodes equivalently to this one,374* but without adding any padding character at the end of the encoded375* byte data.376*377* <p> The encoding scheme of this encoder instance is unaffected by378* this invocation. The returned encoder instance should be used for379* non-padding encoding operation.380*381* @return an equivalent encoder that encodes without adding any382* padding character at the end383*/384public Encoder withoutPadding() {385if (!doPadding)386return this;387return new Encoder(isURL, newline, linemax, false);388}389390private int encode0(byte[] src, int off, int end, byte[] dst) {391char[] base64 = isURL ? toBase64URL : toBase64;392int sp = off;393int slen = (end - off) / 3 * 3;394int sl = off + slen;395if (linemax > 0 && slen > linemax / 4 * 3)396slen = linemax / 4 * 3;397int dp = 0;398while (sp < sl) {399int sl0 = Math.min(sp + slen, sl);400for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {401int bits = (src[sp0++] & 0xff) << 16 |402(src[sp0++] & 0xff) << 8 |403(src[sp0++] & 0xff);404dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];405dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];406dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f];407dst[dp0++] = (byte)base64[bits & 0x3f];408}409int dlen = (sl0 - sp) / 3 * 4;410dp += dlen;411sp = sl0;412if (dlen == linemax && sp < end) {413for (byte b : newline){414dst[dp++] = b;415}416}417}418if (sp < end) { // 1 or 2 leftover bytes419int b0 = src[sp++] & 0xff;420dst[dp++] = (byte)base64[b0 >> 2];421if (sp == end) {422dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];423if (doPadding) {424dst[dp++] = '=';425dst[dp++] = '=';426}427} else {428int b1 = src[sp++] & 0xff;429dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];430dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];431if (doPadding) {432dst[dp++] = '=';433}434}435}436return dp;437}438}439440/**441* This class implements a decoder for decoding byte data using the442* Base64 encoding scheme as specified in RFC 4648 and RFC 2045.443*444* <p> The Base64 padding character {@code '='} is accepted and445* interpreted as the end of the encoded byte data, but is not446* required. So if the final unit of the encoded byte data only has447* two or three Base64 characters (without the corresponding padding448* character(s) padded), they are decoded as if followed by padding449* character(s). If there is a padding character present in the450* final unit, the correct number of padding character(s) must be451* present, otherwise {@code IllegalArgumentException} (452* {@code IOException} when reading from a Base64 stream) is thrown453* during decoding.454*455* <p> Instances of {@link Decoder} class are safe for use by456* multiple concurrent threads.457*458* <p> Unless otherwise noted, passing a {@code null} argument to459* a method of this class will cause a460* {@link java.lang.NullPointerException NullPointerException} to461* be thrown.462*463* @see Encoder464* @since 1.8465*/466public static class Decoder {467468private final boolean isURL;469private final boolean isMIME;470471private Decoder(boolean isURL, boolean isMIME) {472this.isURL = isURL;473this.isMIME = isMIME;474}475476/**477* Lookup table for decoding unicode characters drawn from the478* "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into479* their 6-bit positive integer equivalents. Characters that480* are not in the Base64 alphabet but fall within the bounds of481* the array are encoded to -1.482*483*/484private static final int[] fromBase64 = new int[256];485static {486Arrays.fill(fromBase64, -1);487for (int i = 0; i < Encoder.toBase64.length; i++)488fromBase64[Encoder.toBase64[i]] = i;489fromBase64['='] = -2;490}491492/**493* Lookup table for decoding "URL and Filename safe Base64 Alphabet"494* as specified in Table2 of the RFC 4648.495*/496private static final int[] fromBase64URL = new int[256];497498static {499Arrays.fill(fromBase64URL, -1);500for (int i = 0; i < Encoder.toBase64URL.length; i++)501fromBase64URL[Encoder.toBase64URL[i]] = i;502fromBase64URL['='] = -2;503}504505static final Decoder RFC4648 = new Decoder(false, false);506static final Decoder RFC4648_URLSAFE = new Decoder(true, false);507static final Decoder RFC2045 = new Decoder(false, true);508509/**510* Decodes all bytes from the input byte array using the {@link Base64}511* encoding scheme, writing the results into a newly-allocated output512* byte array. The returned byte array is of the length of the resulting513* bytes.514*515* @param src516* the byte array to decode517*518* @return A newly-allocated byte array containing the decoded bytes.519*520* @throws IllegalArgumentException521* if {@code src} is not in valid Base64 scheme522*/523public byte[] decode(byte[] src) {524byte[] dst = new byte[outLength(src, 0, src.length)];525int ret = decode0(src, 0, src.length, dst);526if (ret != dst.length) {527dst = Arrays.copyOf(dst, ret);528}529return dst;530}531532/**533* Decodes a Base64 encoded String into a newly-allocated byte array534* using the {@link Base64} encoding scheme.535*536* <p> An invocation of this method has exactly the same effect as invoking537* {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}538*539* @param src540* the string to decode541*542* @return A newly-allocated byte array containing the decoded bytes.543*544* @throws IllegalArgumentException545* if {@code src} is not in valid Base64 scheme546*/547public byte[] decode(String src) {548return decode(src.getBytes(StandardCharsets.ISO_8859_1));549}550551/**552* Decodes all bytes from the input byte array using the {@link Base64}553* encoding scheme, writing the results into the given output byte array,554* starting at offset 0.555*556* <p> It is the responsibility of the invoker of this method to make557* sure the output byte array {@code dst} has enough space for decoding558* all bytes from the input byte array. No bytes will be be written to559* the output byte array if the output byte array is not big enough.560*561* <p> If the input byte array is not in valid Base64 encoding scheme562* then some bytes may have been written to the output byte array before563* IllegalargumentException is thrown.564*565* @param src566* the byte array to decode567* @param dst568* the output byte array569*570* @return The number of bytes written to the output byte array571*572* @throws IllegalArgumentException573* if {@code src} is not in valid Base64 scheme, or {@code dst}574* does not have enough space for decoding all input bytes.575*/576public int decode(byte[] src, byte[] dst) {577int len = outLength(src, 0, src.length);578if (dst.length < len)579throw new IllegalArgumentException(580"Output byte array is too small for decoding all input bytes");581return decode0(src, 0, src.length, dst);582}583584/**585* Decodes all bytes from the input byte buffer using the {@link Base64}586* encoding scheme, writing the results into a newly-allocated ByteBuffer.587*588* <p> Upon return, the source buffer's position will be updated to589* its limit; its limit will not have been changed. The returned590* output buffer's position will be zero and its limit will be the591* number of resulting decoded bytes592*593* <p> {@code IllegalArgumentException} is thrown if the input buffer594* is not in valid Base64 encoding scheme. The position of the input595* buffer will not be advanced in this case.596*597* @param buffer598* the ByteBuffer to decode599*600* @return A newly-allocated byte buffer containing the decoded bytes601*602* @throws IllegalArgumentException603* if {@code src} is not in valid Base64 scheme.604*/605public ByteBuffer decode(ByteBuffer buffer) {606int pos0 = buffer.position();607try {608byte[] src;609int sp, sl;610if (buffer.hasArray()) {611src = buffer.array();612sp = buffer.arrayOffset() + buffer.position();613sl = buffer.arrayOffset() + buffer.limit();614buffer.position(buffer.limit());615} else {616src = new byte[buffer.remaining()];617buffer.get(src);618sp = 0;619sl = src.length;620}621byte[] dst = new byte[outLength(src, sp, sl)];622return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));623} catch (IllegalArgumentException iae) {624buffer.position(pos0);625throw iae;626}627}628629/**630* Returns an input stream for decoding {@link Base64} encoded byte stream.631*632* <p> The {@code read} methods of the returned {@code InputStream} will633* throw {@code IOException} when reading bytes that cannot be decoded.634*635* <p> Closing the returned input stream will close the underlying636* input stream.637*638* @param is639* the input stream640*641* @return the input stream for decoding the specified Base64 encoded642* byte stream643*/644public InputStream wrap(InputStream is) {645Objects.requireNonNull(is);646return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);647}648649private int outLength(byte[] src, int sp, int sl) {650int[] base64 = isURL ? fromBase64URL : fromBase64;651int paddings = 0;652int len = sl - sp;653if (len == 0)654return 0;655if (len < 2) {656if (isMIME && base64[0] == -1)657return 0;658throw new IllegalArgumentException(659"Input byte[] should at least have 2 bytes for base64 bytes");660}661if (isMIME) {662// scan all bytes to fill out all non-alphabet. a performance663// trade-off of pre-scan or Arrays.copyOf664int n = 0;665while (sp < sl) {666int b = src[sp++] & 0xff;667if (b == '=') {668len -= (sl - sp + 1);669break;670}671if ((b = base64[b]) == -1)672n++;673}674len -= n;675} else {676if (src[sl - 1] == '=') {677paddings++;678if (src[sl - 2] == '=')679paddings++;680}681}682if (paddings == 0 && (len & 0x3) != 0)683paddings = 4 - (len & 0x3);684return 3 * ((len + 3) / 4) - paddings;685}686687private int decode0(byte[] src, int sp, int sl, byte[] dst) {688int[] base64 = isURL ? fromBase64URL : fromBase64;689int dp = 0;690int bits = 0;691int shiftto = 18; // pos of first byte of 4-byte atom692while (sp < sl) {693int b = src[sp++] & 0xff;694if ((b = base64[b]) < 0) {695if (b == -2) { // padding byte '='696// = shiftto==18 unnecessary padding697// x= shiftto==12 a dangling single x698// x to be handled together with non-padding case699// xx= shiftto==6&&sp==sl missing last =700// xx=y shiftto==6 last is not =701if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||702shiftto == 18) {703throw new IllegalArgumentException(704"Input byte array has wrong 4-byte ending unit");705}706break;707}708if (isMIME) // skip if for rfc2045709continue;710else711throw new IllegalArgumentException(712"Illegal base64 character " +713Integer.toString(src[sp - 1], 16));714}715bits |= (b << shiftto);716shiftto -= 6;717if (shiftto < 0) {718dst[dp++] = (byte)(bits >> 16);719dst[dp++] = (byte)(bits >> 8);720dst[dp++] = (byte)(bits);721shiftto = 18;722bits = 0;723}724}725// reached end of byte array or hit padding '=' characters.726if (shiftto == 6) {727dst[dp++] = (byte)(bits >> 16);728} else if (shiftto == 0) {729dst[dp++] = (byte)(bits >> 16);730dst[dp++] = (byte)(bits >> 8);731} else if (shiftto == 12) {732// dangling single "x", incorrectly encoded.733throw new IllegalArgumentException(734"Last unit does not have enough valid bits");735}736// anything left is invalid, if is not MIME.737// if MIME, ignore all non-base64 character738while (sp < sl) {739if (isMIME && base64[src[sp++]] < 0)740continue;741throw new IllegalArgumentException(742"Input byte array has incorrect ending byte at " + sp);743}744return dp;745}746}747748/*749* An output stream for encoding bytes into the Base64.750*/751private static class EncOutputStream extends FilterOutputStream {752753private int leftover = 0;754private int b0, b1, b2;755private boolean closed = false;756757private final char[] base64; // byte->base64 mapping758private final byte[] newline; // line separator, if needed759private final int linemax;760private final boolean doPadding;// whether or not to pad761private int linepos = 0;762763EncOutputStream(OutputStream os, char[] base64,764byte[] newline, int linemax, boolean doPadding) {765super(os);766this.base64 = base64;767this.newline = newline;768this.linemax = linemax;769this.doPadding = doPadding;770}771772@Override773public void write(int b) throws IOException {774byte[] buf = new byte[1];775buf[0] = (byte)(b & 0xff);776write(buf, 0, 1);777}778779private void checkNewline() throws IOException {780if (linepos == linemax) {781out.write(newline);782linepos = 0;783}784}785786@Override787public void write(byte[] b, int off, int len) throws IOException {788if (closed)789throw new IOException("Stream is closed");790if (off < 0 || len < 0 || len > b.length - off)791throw new ArrayIndexOutOfBoundsException();792if (len == 0)793return;794if (leftover != 0) {795if (leftover == 1) {796b1 = b[off++] & 0xff;797len--;798if (len == 0) {799leftover++;800return;801}802}803b2 = b[off++] & 0xff;804len--;805checkNewline();806out.write(base64[b0 >> 2]);807out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);808out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]);809out.write(base64[b2 & 0x3f]);810linepos += 4;811}812int nBits24 = len / 3;813leftover = len - (nBits24 * 3);814while (nBits24-- > 0) {815checkNewline();816int bits = (b[off++] & 0xff) << 16 |817(b[off++] & 0xff) << 8 |818(b[off++] & 0xff);819out.write(base64[(bits >>> 18) & 0x3f]);820out.write(base64[(bits >>> 12) & 0x3f]);821out.write(base64[(bits >>> 6) & 0x3f]);822out.write(base64[bits & 0x3f]);823linepos += 4;824}825if (leftover == 1) {826b0 = b[off++] & 0xff;827} else if (leftover == 2) {828b0 = b[off++] & 0xff;829b1 = b[off++] & 0xff;830}831}832833@Override834public void close() throws IOException {835if (!closed) {836closed = true;837if (leftover == 1) {838checkNewline();839out.write(base64[b0 >> 2]);840out.write(base64[(b0 << 4) & 0x3f]);841if (doPadding) {842out.write('=');843out.write('=');844}845} else if (leftover == 2) {846checkNewline();847out.write(base64[b0 >> 2]);848out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);849out.write(base64[(b1 << 2) & 0x3f]);850if (doPadding) {851out.write('=');852}853}854leftover = 0;855out.close();856}857}858}859860/*861* An input stream for decoding Base64 bytes862*/863private static class DecInputStream extends InputStream {864865private final InputStream is;866private final boolean isMIME;867private final int[] base64; // base64 -> byte mapping868private int bits = 0; // 24-bit buffer for decoding869private int nextin = 18; // next available "off" in "bits" for input;870// -> 18, 12, 6, 0871private int nextout = -8; // next available "off" in "bits" for output;872// -> 8, 0, -8 (no byte for output)873private boolean eof = false;874private boolean closed = false;875876DecInputStream(InputStream is, int[] base64, boolean isMIME) {877this.is = is;878this.base64 = base64;879this.isMIME = isMIME;880}881882private byte[] sbBuf = new byte[1];883884@Override885public int read() throws IOException {886return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;887}888889@Override890public int read(byte[] b, int off, int len) throws IOException {891if (closed)892throw new IOException("Stream is closed");893if (eof && nextout < 0) // eof and no leftover894return -1;895if (off < 0 || len < 0 || len > b.length - off)896throw new IndexOutOfBoundsException();897int oldOff = off;898if (nextout >= 0) { // leftover output byte(s) in bits buf899do {900if (len == 0)901return off - oldOff;902b[off++] = (byte)(bits >> nextout);903len--;904nextout -= 8;905} while (nextout >= 0);906bits = 0;907}908while (len > 0) {909int v = is.read();910if (v == -1) {911eof = true;912if (nextin != 18) {913if (nextin == 12)914throw new IOException("Base64 stream has one un-decoded dangling byte.");915// treat ending xx/xxx without padding character legal.916// same logic as v == '=' below917b[off++] = (byte)(bits >> (16));918len--;919if (nextin == 0) { // only one padding byte920if (len == 0) { // no enough output space921bits >>= 8; // shift to lowest byte922nextout = 0;923} else {924b[off++] = (byte) (bits >> 8);925}926}927}928if (off == oldOff)929return -1;930else931return off - oldOff;932}933if (v == '=') { // padding byte(s)934// = shiftto==18 unnecessary padding935// x= shiftto==12 dangling x, invalid unit936// xx= shiftto==6 && missing last '='937// xx=y or last is not '='938if (nextin == 18 || nextin == 12 ||939nextin == 6 && is.read() != '=') {940throw new IOException("Illegal base64 ending sequence:" + nextin);941}942b[off++] = (byte)(bits >> (16));943len--;944if (nextin == 0) { // only one padding byte945if (len == 0) { // no enough output space946bits >>= 8; // shift to lowest byte947nextout = 0;948} else {949b[off++] = (byte) (bits >> 8);950}951}952eof = true;953break;954}955if ((v = base64[v]) == -1) {956if (isMIME) // skip if for rfc2045957continue;958else959throw new IOException("Illegal base64 character " +960Integer.toString(v, 16));961}962bits |= (v << nextin);963if (nextin == 0) {964nextin = 18; // clear for next965nextout = 16;966while (nextout >= 0) {967b[off++] = (byte)(bits >> nextout);968len--;969nextout -= 8;970if (len == 0 && nextout >= 0) { // don't clean "bits"971return off - oldOff;972}973}974bits = 0;975} else {976nextin -= 6;977}978}979return off - oldOff;980}981982@Override983public int available() throws IOException {984if (closed)985throw new IOException("Stream is closed");986return is.available(); // TBD:987}988989@Override990public void close() throws IOException {991if (!closed) {992closed = true;993is.close();994}995}996}997}9989991000