提交 92bd7522 编写于 作者: S sherman

8004088: hg push for bug#4235519 failed to push all files

Summary: pushed all base64 files
Reviewed-by: alanb, mduigou
上级 137e212c
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util;
import java.io.FilterOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
/**
* This class consists exclusively of static methods for obtaining
* encoders and decoders for the Base64 encoding scheme. The
* implementation of this class supports the following types of Base64
* as specified in
* <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
* <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
*
* <p>
* <ul>
* <a name="basic">
* <li><b>Basic</b>
* <p> Uses "The Base64 Alphabet" as specified in Table 1 of
* RFC 4648 and RFC 2045 for encoding and decoding operation.
* The encoder does not add any line feed (line separator)
* character. The decoder rejects data that contains characters
* outside the base64 alphabet.</p></li>
*
* <a name="url">
* <li><b>URL and Filename safe</b>
* <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
* in Table 2 of RFC 4648 for encoding and decoding. The
* encoder does not add any line feed (line separator) character.
* The decoder rejects data that contains characters outside the
* base64 alphabet.</p></li>
*
* <a name="mime">
* <li><b>MIME</b>
* <p> Uses the "The Base64 Alphabet" as specified in Table 1 of
* RFC 2045 for encoding and decoding operation. The encoded output
* must be represented in lines of no more than 76 characters each
* and uses a carriage return {@code '\r'} followed immediately by
* a linefeed {@code '\n'} as the line separator. All line separators
* or other characters not found in the base64 alphabet table are
* ignored in decoding operation.</p></li>
* </ul>
*
* <p> Unless otherwise noted, passing a {@code null} argument to a
* method of this class will cause a {@link java.lang.NullPointerException
* NullPointerException} to be thrown.
*
* @author Xueming Shen
* @since 1.8
*/
public class Base64 {
private Base64() {}
/**
* Returns a {@link Encoder} that encodes using the
* <a href="#basic">Basic</a> type base64 encoding scheme.
*
* @return A Base64 encoder.
*/
public static Encoder getEncoder() {
return Encoder.RFC4648;
}
/**
* Returns a {@link Encoder} that encodes using the
* <a href="#url">URL and Filename safe</a> type base64
* encoding scheme.
*
* @return A Base64 encoder.
*/
public static Encoder getUrlEncoder() {
return Encoder.RFC4648_URLSAFE;
}
/**
* Returns a {@link Encoder} that encodes using the
* <a href="#mime">MIME</a> type base64 encoding scheme.
*
* @return A Base64 encoder.
*/
public static Encoder getMimeEncoder() {
return Encoder.RFC2045;
}
/**
* Returns a {@link Encoder} that encodes using the
* <a href="#mime">MIME</a> type base64 encoding scheme
* with specified line length and line separators.
*
* @param lineLength
* the length of each output line (rounded down to nearest multiple
* of 4). If {@code lineLength <= 0} the output will not be separated
* in lines
* @param lineSeparator
* the line separator for each output line
*
* @return A Base64 encoder.
*
* @throws IllegalArgumentException if {@code lineSeparator} includes any
* character of "The Base64 Alphabet" as specified in Table 1 of
* RFC 2045.
*/
public static Encoder getEncoder(int lineLength, byte[] lineSeparator) {
Objects.requireNonNull(lineSeparator);
int[] base64 = Decoder.fromBase64;
for (byte b : lineSeparator) {
if (base64[b & 0xff] != -1)
throw new IllegalArgumentException(
"Illegal base64 line separator character 0x" + Integer.toString(b, 16));
}
return new Encoder(false, lineSeparator, lineLength >> 2 << 2);
}
/**
* Returns a {@link Decoder} that decodes using the
* <a href="#basic">Basic</a> type base64 encoding scheme.
*
* @return A Base64 decoder.
*/
public static Decoder getDecoder() {
return Decoder.RFC4648;
}
/**
* Returns a {@link Decoder} that decodes using the
* <a href="#url">URL and Filename safe</a> type base64
* encoding scheme.
*
* @return A Base64 decoder.
*/
public static Decoder getUrlDecoder() {
return Decoder.RFC4648_URLSAFE;
}
/**
* Returns a {@link Decoder} that decodes using the
* <a href="#mime">MIME</a> type base64 decoding scheme.
*
* @return A Base64 decoder.
*/
public static Decoder getMimeDecoder() {
return Decoder.RFC2045;
}
/**
* This class implements an encoder for encoding byte data using
* the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
*
* <p> Instances of {@link Encoder} class are safe for use by
* multiple concurrent threads.
*
* <p> Unless otherwise noted, passing a {@code null} argument to
* a method of this class will cause a
* {@link java.lang.NullPointerException NullPointerException} to
* be thrown.
*
* @see Decoder
* @since 1.8
*/
public static class Encoder {
private final byte[] newline;
private final int linemax;
private final boolean isURL;
private Encoder(boolean isURL, byte[] newline, int linemax) {
this.isURL = isURL;
this.newline = newline;
this.linemax = linemax;
}
/**
* This array is a lookup table that translates 6-bit positive integer
* index values into their "Base64 Alphabet" equivalents as specified
* in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
*/
private static final char[] toBase64 = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
/**
* It's the lookup table for "URL and Filename safe Base64" as specified
* in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
* '_'. This table is used when BASE64_URL is specified.
*/
private static final char[] toBase64URL = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
private static final int MIMELINEMAX = 76;
private static final byte[] CRLF = new byte[] {'\r', '\n'};
static final Encoder RFC4648 = new Encoder(false, null, -1);
static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1);
static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX);
/**
* Encodes all bytes from the specified byte array into a newly-allocated
* byte array using the {@link Base64} encoding scheme. The returned byte
* array is of the length of the resulting bytes.
*
* @param src
* the byte array to encode
* @return A newly-allocated byte array containing the resulting
* encoded bytes.
*/
public byte[] encode(byte[] src) {
int len = 4 * ((src.length + 2) / 3); // dst array size
if (linemax > 0) // line separators
len += (len - 1) / linemax * newline.length;
byte[] dst = new byte[len];
int ret = encode0(src, 0, src.length, dst);
if (ret != dst.length)
return Arrays.copyOf(dst, ret);
return dst;
}
/**
* Encodes all bytes from the specified byte array using the
* {@link Base64} encoding scheme, writing the resulting bytes to the
* given output byte array, starting at offset 0.
*
* <p> It is the responsibility of the invoker of this method to make
* sure the output byte array {@code dst} has enough space for encoding
* all bytes from the input byte array. No bytes will be written to the
* output byte array if the output byte array is not big enough.
*
* @param src
* the byte array to encode
* @param dst
* the output byte array
* @return The number of bytes written to the output byte array
*
* @throws IllegalArgumentException if {@code dst} does not have enough
* space for encoding all input bytes.
*/
public int encode(byte[] src, byte[] dst) {
int len = 4 * ((src.length + 2) / 3); // dst array size
if (linemax > 0) {
len += (len - 1) / linemax * newline.length;
}
if (dst.length < len)
throw new IllegalArgumentException(
"Output byte array is too small for encoding all input bytes");
return encode0(src, 0, src.length, dst);
}
/**
* Encodes the specified byte array into a String using the {@link Base64}
* encoding scheme.
*
* <p> This method first encodes all input bytes into a base64 encoded
* byte array and then constructs a new String by using the encoded byte
* array and the {@link java.nio.charset.StandardCharsets.ISO_8859_1 ISO-8859-1}
* charset.
*
* <p> In other words, an invocation of this method has exactly the same
* effect as invoking
* {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
*
* @param src
* the byte array to encode
* @return A String containing the resulting Base64 encoded characters
*/
@SuppressWarnings("deprecation")
public String encodeToString(byte[] src) {
byte[] encoded = encode(src);
return new String(encoded, 0, 0, encoded.length);
}
/**
* Encodes all remaining bytes from the specified byte buffer into
* a newly-allocated ByteBuffer using the {@link Base64} encoding
* scheme.
*
* Upon return, the source buffer's position will be updated to
* its limit; its limit will not have been changed. The returned
* output buffer's position will be zero and its limit will be the
* number of resulting encoded bytes.
*
* @param buffer
* the source ByteBuffer to encode
* @return A newly-allocated byte buffer containing the encoded bytes.
*/
public ByteBuffer encode(ByteBuffer buffer) {
int len = 4 * ((buffer.remaining() + 2) / 3);
if (linemax > 0)
len += (len - 1) / linemax * newline.length;
byte[] dst = new byte[len];
int ret = 0;
if (buffer.hasArray()) {
ret = encode0(buffer.array(),
buffer.arrayOffset() + buffer.position(),
buffer.arrayOffset() + buffer.limit(),
dst);
buffer.position(buffer.limit());
} else {
byte[] src = new byte[buffer.remaining()];
buffer.get(src);
ret = encode0(src, 0, src.length, dst);
}
if (ret != dst.length)
dst = Arrays.copyOf(dst, ret);
return ByteBuffer.wrap(dst);
}
/**
* Encodes as many bytes as possible from the input byte buffer
* using the {@link Base64} encoding scheme, writing the resulting
* bytes to the given output byte buffer.
*
* <p>The buffers are read from, and written to, starting at their
* current positions. Upon return, the input and output buffers'
* positions will be advanced to reflect the bytes read and written,
* but their limits will not be modified.
*
* <p>The encoding operation will stop and return if either all
* remaining bytes in the input buffer have been encoded and written
* to the output buffer, or the output buffer has insufficient space
* to encode any more input bytes. The encoding operation can be
* continued, if there is more bytes in input buffer to be encoded,
* by invoking this method again with an output buffer that has more
* {@linkplain Buffer#remaining remaining} bytes. This is typically
* done by draining any encoded bytes from the output buffer. The
* value returned from last invocation needs to be passed in as the
* third parameter {@code bytesOut} if it is to continue an unfinished
* encoding, 0 otherwise.
*
* <p><b>Recommended Usage Example</b>
* <pre>
* ByteBuffer src = ...;
* ByteBuffer dst = ...;
* Base64.Encoder enc = Base64.getMimeDecoder();
*
* int bytesOut = 0;
* while (src.hasRemaining()) {
* // clear output buffer for decoding
* dst.clear();
* bytesOut = enc.encode(src, dst, bytesOut);
*
* // read encoded bytes out of "dst"
* dst.flip();
* ...
* }
* </pre>
*
* @param src
* the input byte buffer to encode
* @param dst
* the output byte buffer
* @param bytesOut
* the return value of last invocation if this is to continue
* an unfinished encoding operation, 0 otherwise
* @return The sum total of {@code bytesOut} and the number of bytes
* written to the output ByteBuffer during this invocation.
*/
public int encode(ByteBuffer src, ByteBuffer dst, int bytesOut) {
if (src.hasArray() && dst.hasArray())
return encodeArray(src, dst, bytesOut);
return encodeBuffer(src, dst, bytesOut);
}
/**
* Wraps an output stream for encoding byte data using the {@link Base64}
* encoding scheme.
*
* <p> It is recommended to promptly close the returned output stream after
* use, during which it will flush all possible leftover bytes to the underlying
* output stream. Closing the returned output stream will close the underlying
* output stream.
*
* @param os
* the output stream.
* @return the output stream for encoding the byte data into the
* specified Base64 encoded format
*/
public OutputStream wrap(OutputStream os) {
return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
newline, linemax);
}
private int encodeArray(ByteBuffer src, ByteBuffer dst, int bytesOut) {
char[] base64 = isURL? toBase64URL : toBase64;
byte[] sa = src.array();
int sp = src.arrayOffset() + src.position();
int sl = src.arrayOffset() + src.limit();
byte[] da = dst.array();
int dp = dst.arrayOffset() + dst.position();
int dl = dst.arrayOffset() + dst.limit();
int dp00 = dp;
int dpos = 0; // dp of each line
if (linemax > 0 && bytesOut > 0)
dpos = bytesOut % (linemax + newline.length);
try {
if (dpos == linemax && sp < src.limit()) {
if (dp + newline.length > dl)
return dp - dp00 + bytesOut;
for (byte b : newline){
dst.put(dp++, b);
}
dpos = 0;
}
sl = sp + (sl - sp) / 3 * 3;
while (sp < sl) {
int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3
: sl - sp;
int sl0 = Math.min(sp + slen, sl);
for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
if (dp0 + 4 > dl) {
sp = sp0; dp = dp0;
return dp0 - dp00 + bytesOut;
}
int bits = (sa[sp0++] & 0xff) << 16 |
(sa[sp0++] & 0xff) << 8 |
(sa[sp0++] & 0xff);
da[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
da[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
da[dp0++] = (byte)base64[(bits >>> 6) & 0x3f];
da[dp0++] = (byte)base64[bits & 0x3f];
}
int n = (sl0 - sp) / 3 * 4;
dpos += n;
dp += n;
sp = sl0;
if (dpos == linemax && sp < src.limit()) {
if (dp + newline.length > dl)
return dp - dp00 + bytesOut;
for (byte b : newline){
da[dp++] = b;
}
dpos = 0;
}
}
sl = src.arrayOffset() + src.limit();
if (sp < sl && dl >= dp + 4) { // 1 or 2 leftover bytes
int b0 = sa[sp++] & 0xff;
da[dp++] = (byte)base64[b0 >> 2];
if (sp == sl) {
da[dp++] = (byte)base64[(b0 << 4) & 0x3f];
da[dp++] = '=';
da[dp++] = '=';
} else {
int b1 = sa[sp++] & 0xff;
da[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
da[dp++] = (byte)base64[(b1 << 2) & 0x3f];
da[dp++] = '=';
}
}
return dp - dp00 + bytesOut;
} finally {
src.position(sp - src.arrayOffset());
dst.position(dp - dst.arrayOffset());
}
}
private int encodeBuffer(ByteBuffer src, ByteBuffer dst, int bytesOut) {
char[] base64 = isURL? toBase64URL : toBase64;
int sp = src.position();
int sl = src.limit();
int dp = dst.position();
int dl = dst.limit();
int dp00 = dp;
int dpos = 0; // dp of each line
if (linemax > 0 && bytesOut > 0)
dpos = bytesOut % (linemax + newline.length);
try {
if (dpos == linemax && sp < src.limit()) {
if (dp + newline.length > dl)
return dp - dp00 + bytesOut;
for (byte b : newline){
dst.put(dp++, b);
}
dpos = 0;
}
sl = sp + (sl - sp) / 3 * 3;
while (sp < sl) {
int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3
: sl - sp;
int sl0 = Math.min(sp + slen, sl);
for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
if (dp0 + 4 > dl) {
sp = sp0; dp = dp0;
return dp0 - dp00 + bytesOut;
}
int bits = (src.get(sp0++) & 0xff) << 16 |
(src.get(sp0++) & 0xff) << 8 |
(src.get(sp0++) & 0xff);
dst.put(dp0++, (byte)base64[(bits >>> 18) & 0x3f]);
dst.put(dp0++, (byte)base64[(bits >>> 12) & 0x3f]);
dst.put(dp0++, (byte)base64[(bits >>> 6) & 0x3f]);
dst.put(dp0++, (byte)base64[bits & 0x3f]);
}
int n = (sl0 - sp) / 3 * 4;
dpos += n;
dp += n;
sp = sl0;
if (dpos == linemax && sp < src.limit()) {
if (dp + newline.length > dl)
return dp - dp00 + bytesOut;
for (byte b : newline){
dst.put(dp++, b);
}
dpos = 0;
}
}
if (sp < src.limit() && dl >= dp + 4) { // 1 or 2 leftover bytes
int b0 = src.get(sp++) & 0xff;
dst.put(dp++, (byte)base64[b0 >> 2]);
if (sp == src.limit()) {
dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f]);
dst.put(dp++, (byte)'=');
dst.put(dp++, (byte)'=');
} else {
int b1 = src.get(sp++) & 0xff;
dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
dst.put(dp++, (byte)base64[(b1 << 2) & 0x3f]);
dst.put(dp++, (byte)'=');
}
}
return dp - dp00 + bytesOut;
} finally {
src.position(sp);
dst.position(dp);
}
}
private int encode0(byte[] src, int off, int end, byte[] dst) {
char[] base64 = isURL ? toBase64URL : toBase64;
int sp = off;
int slen = (end - off) / 3 * 3;
int sl = off + slen;
if (linemax > 0 && slen > linemax / 4 * 3)
slen = linemax / 4 * 3;
int dp = 0;
while (sp < sl) {
int sl0 = Math.min(sp + slen, sl);
for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
int bits = (src[sp0++] & 0xff) << 16 |
(src[sp0++] & 0xff) << 8 |
(src[sp0++] & 0xff);
dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f];
dst[dp0++] = (byte)base64[bits & 0x3f];
}
int dlen = (sl0 - sp) / 3 * 4;
dp += dlen;
sp = sl0;
if (dlen == linemax && sp < end) {
for (byte b : newline){
dst[dp++] = b;
}
}
}
if (sp < end) { // 1 or 2 leftover bytes
int b0 = src[sp++] & 0xff;
dst[dp++] = (byte)base64[b0 >> 2];
if (sp == end) {
dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
dst[dp++] = '=';
dst[dp++] = '=';
} else {
int b1 = src[sp++] & 0xff;
dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
dst[dp++] = '=';
}
}
return dp;
}
}
/**
* This class implements a decoder for decoding byte data using the
* Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
*
* <p> Instances of {@link Decoder} class are safe for use by
* multiple concurrent threads.
*
* <p> Unless otherwise noted, passing a {@code null} argument to
* a method of this class will cause a
* {@link java.lang.NullPointerException NullPointerException} to
* be thrown.
*
* @see Encoder
* @since 1.8
*/
public static class Decoder {
private final boolean isURL;
private final boolean isMIME;
private Decoder(boolean isURL, boolean isMIME) {
this.isURL = isURL;
this.isMIME = isMIME;
}
/**
* Lookup table for decoding unicode characters drawn from the
* "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
* their 6-bit positive integer equivalents. Characters that
* are not in the Base64 alphabet but fall within the bounds of
* the array are encoded to -1.
*
*/
private static final int[] fromBase64 = new int[256];
static {
Arrays.fill(fromBase64, -1);
for (int i = 0; i < Encoder.toBase64.length; i++)
fromBase64[Encoder.toBase64[i]] = i;
fromBase64['='] = -2;
}
/**
* Lookup table for decoding "URL and Filename safe Base64 Alphabet"
* as specified in Table2 of the RFC 4648.
*/
private static final int[] fromBase64URL = new int[256];
static {
Arrays.fill(fromBase64URL, -1);
for (int i = 0; i < Encoder.toBase64URL.length; i++)
fromBase64URL[Encoder.toBase64URL[i]] = i;
fromBase64URL['='] = -2;
}
static final Decoder RFC4648 = new Decoder(false, false);
static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
static final Decoder RFC2045 = new Decoder(false, true);
/**
* Decodes all bytes from the input byte array using the {@link Base64}
* encoding scheme, writing the results into a newly-allocated output
* byte array. The returned byte array is of the length of the resulting
* bytes.
*
* @param src
* the byte array to decode
*
* @return A newly-allocated byte array containing the decoded bytes.
*
* @throws IllegalArgumentException
* if {@code src} is not in valid Base64 scheme
*/
public byte[] decode(byte[] src) {
byte[] dst = new byte[outLength(src, 0, src.length)];
int ret = decode0(src, 0, src.length, dst);
if (ret != dst.length) {
dst = Arrays.copyOf(dst, ret);
}
return dst;
}
/**
* Decodes a Base64 encoded String into a newly-allocated byte array
* using the {@link Base64} encoding scheme.
*
* <p> An invocation of this method has exactly the same effect as invoking
* {@code return decode(src.getBytes(StandardCharsets.ISO_8859_1))}
*
* @param src
* the string to decode
*
* @return A newly-allocated byte array containing the decoded bytes.
*
* @throws IllegalArgumentException
* if {@code src} is not in valid Base64 scheme
*/
public byte[] decode(String src) {
return decode(src.getBytes(StandardCharsets.ISO_8859_1));
}
/**
* Decodes all bytes from the input byte array using the {@link Base64}
* encoding scheme, writing the results into the given output byte array,
* starting at offset 0.
*
* <p> It is the responsibility of the invoker of this method to make
* sure the output byte array {@code dst} has enough space for decoding
* all bytes from the input byte array. No bytes will be be written to
* the output byte array if the output byte array is not big enough.
*
* <p> If the input byte array is not in valid Base64 encoding scheme
* then some bytes may have been written to the output byte array before
* IllegalargumentException is thrown.
*
* @param src
* the byte array to decode
* @param dst
* the output byte array
*
* @return The number of bytes written to the output byte array
*
* @throws IllegalArgumentException
* if {@code src} is not in valid Base64 scheme, or {@code dst}
* does not have enough space for decoding all input bytes.
*/
public int decode(byte[] src, byte[] dst) {
int len = outLength(src, 0, src.length);
if (dst.length < len)
throw new IllegalArgumentException(
"Output byte array is too small for decoding all input bytes");
return decode0(src, 0, src.length, dst);
}
/**
* Decodes all bytes from the input byte buffer using the {@link Base64}
* encoding scheme, writing the results into a newly-allocated ByteBuffer.
*
* <p> Upon return, the source buffer's position will be updated to
* its limit; its limit will not have been changed. The returned
* output buffer's position will be zero and its limit will be the
* number of resulting decoded bytes
*
* @param buffer
* the ByteBuffer to decode
*
* @return A newly-allocated byte buffer containing the decoded bytes
*
* @throws IllegalArgumentException
* if {@code src} is not in valid Base64 scheme.
*/
public ByteBuffer decode(ByteBuffer buffer) {
int pos0 = buffer.position();
try {
byte[] src;
int sp, sl;
if (buffer.hasArray()) {
src = buffer.array();
sp = buffer.arrayOffset() + buffer.position();
sl = buffer.arrayOffset() + buffer.limit();
buffer.position(buffer.limit());
} else {
src = new byte[buffer.remaining()];
buffer.get(src);
sp = 0;
sl = src.length;
}
byte[] dst = new byte[outLength(src, sp, sl)];
return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
} catch (IllegalArgumentException iae) {
buffer.position(pos0);
throw iae;
}
}
/**
* Decodes as many bytes as possible from the input byte buffer
* using the {@link Base64} encoding scheme, writing the resulting
* bytes to the given output byte buffer.
*
* <p>The buffers are read from, and written to, starting at their
* current positions. Upon return, the input and output buffers'
* positions will be advanced to reflect the bytes read and written,
* but their limits will not be modified.
*
* <p> If the input buffer is not in valid Base64 encoding scheme
* then some bytes may have been written to the output buffer
* before IllegalArgumentException is thrown. The positions of
* both input and output buffer will not be advanced in this case.
*
* <p>The decoding operation will end and return if all remaining
* bytes in the input buffer have been decoded and written to the
* output buffer.
*
* <p> The decoding operation will stop and return if the output
* buffer has insufficient space to decode any more input bytes.
* The decoding operation can be continued, if there is more bytes
* in input buffer to be decoded, by invoking this method again with
* an output buffer that has more {@linkplain Buffer#remaining remaining}
* bytes.This is typically done by draining any decoded bytes from the
* output buffer.
*
* <p><b>Recommended Usage Example</b>
* <pre>
* ByteBuffer src = ...;
* ByteBuffer dst = ...;
* Base64.Decoder dec = Base64.getDecoder();
*
* while (src.hasRemaining()) {
*
* // prepare the output byte buffer
* dst.clear();
* dec.decode(src, dst);
*
* // read bytes from the output buffer
* dst.flip();
* ...
* }
* </pre>
*
* @param src
* the input byte buffer to decode
* @param dst
* the output byte buffer
*
* @return The number of bytes written to the output byte buffer during
* this decoding invocation
*
* @throws IllegalArgumentException
* if {@code src} is not in valid Base64 scheme.
*/
public int decode(ByteBuffer src, ByteBuffer dst) {
int sp0 = src.position();
int dp0 = dst.position();
try {
if (src.hasArray() && dst.hasArray())
return decodeArray(src, dst);
return decodeBuffer(src, dst);
} catch (IllegalArgumentException iae) {
src.position(sp0);
dst.position(dp0);
throw iae;
}
}
/**
* Returns an input stream for decoding {@link Base64} encoded byte stream.
*
* <p> Closing the returned input stream will close the underlying
* input stream.
*
* @param is
* the input stream
*
* @return the input stream for decoding the specified Base64 encoded
* byte stream
*/
public InputStream wrap(InputStream is) {
return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
}
private int decodeArray(ByteBuffer src, ByteBuffer dst) {
int[] base64 = isURL ? fromBase64URL : fromBase64;
int bits = 0;
int shiftto = 18; // pos of first byte of 4-byte atom
byte[] sa = src.array();
int sp = src.arrayOffset() + src.position();
int sl = src.arrayOffset() + src.limit();
byte[] da = dst.array();
int dp = dst.arrayOffset() + dst.position();
int dl = dst.arrayOffset() + dst.limit();
int dp0 = dp;
int mark = sp;
boolean padding = false;
try {
while (sp < sl) {
int b = sa[sp++] & 0xff;
if ((b = base64[b]) < 0) {
if (b == -2) { // padding byte
padding = true;
break;
}
if (isMIME) // skip if for rfc2045
continue;
else
throw new IllegalArgumentException(
"Illegal base64 character " +
Integer.toString(sa[sp - 1], 16));
}
bits |= (b << shiftto);
shiftto -= 6;
if (shiftto < 0) {
if (dl < dp + 3)
return dp;
da[dp++] = (byte)(bits >> 16);
da[dp++] = (byte)(bits >> 8);
da[dp++] = (byte)(bits);
shiftto = 18;
bits = 0;
mark = sp;
}
}
if (shiftto == 6) {
if (dl - dp < 1)
return dp;
if (padding && (sp + 1 != sl || sa[sp++] != '='))
throw new IllegalArgumentException(
"Input buffer has wrong 4-byte ending unit");
da[dp++] = (byte)(bits >> 16);
mark = sp;
} else if (shiftto == 0) {
if (dl - dp < 2)
return dp;
if (padding && sp != sl)
throw new IllegalArgumentException(
"Input buffer has wrong 4-byte ending unit");
da[dp++] = (byte)(bits >> 16);
da[dp++] = (byte)(bits >> 8);
mark = sp;
} else if (padding || shiftto != 18) {
throw new IllegalArgumentException(
"Last unit does not have enough valid bits");
}
return dp - dp0;
} finally {
src.position(mark);
dst.position(dp);
}
}
private int decodeBuffer(ByteBuffer src, ByteBuffer dst) {
int[] base64 = isURL ? fromBase64URL : fromBase64;
int bits = 0;
int shiftto = 18; // pos of first byte of 4-byte atom
int sp = src.position();
int sl = src.limit();
int dp = dst.position();
int dl = dst.limit();
int dp0 = dp;
int mark = sp;
boolean padding = false;
try {
while (sp < sl) {
int b = src.get(sp++) & 0xff;
if ((b = base64[b]) < 0) {
if (b == -2) { // padding byte
padding = true;
break;
}
if (isMIME) // skip if for rfc2045
continue;
else
throw new IllegalArgumentException(
"Illegal base64 character " +
Integer.toString(src.get(sp - 1), 16));
}
bits |= (b << shiftto);
shiftto -= 6;
if (shiftto < 0) {
if (dl < dp + 3)
return dp;
dst.put(dp++, (byte)(bits >> 16));
dst.put(dp++, (byte)(bits >> 8));
dst.put(dp++, (byte)(bits));
shiftto = 18;
bits = 0;
mark = sp;
}
}
if (shiftto == 6) {
if (dl - dp < 1)
return dp;
if (padding && (sp + 1 != sl || src.get(sp++) != '='))
throw new IllegalArgumentException(
"Input buffer has wrong 4-byte ending unit");
dst.put(dp++, (byte)(bits >> 16));
mark = sp;
} else if (shiftto == 0) {
if (dl - dp < 2)
return dp;
if (padding && sp != sl)
throw new IllegalArgumentException(
"Input buffer has wrong 4-byte ending unit");
dst.put(dp++, (byte)(bits >> 16));
dst.put(dp++, (byte)(bits >> 8));
mark = sp;
} else if (padding || shiftto != 18) {
throw new IllegalArgumentException(
"Last unit does not have enough valid bits");
}
return dp - dp0;
} finally {
src.position(mark);
dst.position(dp);
}
}
private int outLength(byte[] src, int sp, int sl) {
int[] base64 = isURL ? fromBase64URL : fromBase64;
int paddings = 0;
int len = sl - sp;
if (len == 0)
return 0;
if (len < 2)
throw new IllegalArgumentException(
"Input byte[] should at least have 2 bytes for base64 bytes");
if (src[sl - 1] == '=') {
paddings++;
if (src[sl - 2] == '=')
paddings++;
}
if (isMIME) {
// scan all bytes to fill out all non-alphabet. a performance
// trade-off of pre-scan or Arrays.copyOf
int n = 0;
while (sp < sl) {
int b = src[sp++] & 0xff;
if (b == '=')
break;
if ((b = base64[b]) == -1)
n++;
}
len -= n;
}
if (paddings == 0 && (len & 0x3) != 0)
paddings = 4 - (len & 0x3);
return 3 * ((len + 3) / 4) - paddings;
}
private int decode0(byte[] src, int sp, int sl, byte[] dst) {
int[] base64 = isURL ? fromBase64URL : fromBase64;
int dp = 0;
int bits = 0;
int shiftto = 18; // pos of first byte of 4-byte atom
boolean padding = false;
while (sp < sl) {
int b = src[sp++] & 0xff;
if ((b = base64[b]) < 0) {
if (b == -2) { // padding byte
padding = true;
break;
}
if (isMIME) // skip if for rfc2045
continue;
else
throw new IllegalArgumentException(
"Illegal base64 character " +
Integer.toString(src[sp - 1], 16));
}
bits |= (b << shiftto);
shiftto -= 6;
if (shiftto < 0) {
dst[dp++] = (byte)(bits >> 16);
dst[dp++] = (byte)(bits >> 8);
dst[dp++] = (byte)(bits);
shiftto = 18;
bits = 0;
}
}
// reach end of byte arry or hit padding '=' characters.
// if '=' presents, they must be the last one or two.
if (shiftto == 6) { // xx==
if (padding && (sp + 1 != sl || src[sp] != '='))
throw new IllegalArgumentException(
"Input byte array has wrong 4-byte ending unit");
dst[dp++] = (byte)(bits >> 16);
} else if (shiftto == 0) { // xxx=
if (padding && sp != sl)
throw new IllegalArgumentException(
"Input byte array has wrong 4-byte ending unit");
dst[dp++] = (byte)(bits >> 16);
dst[dp++] = (byte)(bits >> 8);
} else if (padding || shiftto != 18) {
throw new IllegalArgumentException(
"last unit does not have enough bytes");
}
return dp;
}
}
/*
* An output stream for encoding bytes into the Base64.
*/
private static class EncOutputStream extends FilterOutputStream {
private int leftover = 0;
private int b0, b1, b2;
private boolean closed = false;
private final char[] base64; // byte->base64 mapping
private final byte[] newline; // line separator, if needed
private final int linemax;
private int linepos = 0;
EncOutputStream(OutputStream os,
char[] base64, byte[] newline, int linemax) {
super(os);
this.base64 = base64;
this.newline = newline;
this.linemax = linemax;
}
@Override
public void write(int b) throws IOException {
byte[] buf = new byte[1];
buf[0] = (byte)(b & 0xff);
write(buf, 0, 1);
}
private void checkNewline() throws IOException {
if (linepos == linemax) {
out.write(newline);
linepos = 0;
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (closed)
throw new IOException("Stream is closed");
if (off < 0 || len < 0 || off + len > b.length)
throw new ArrayIndexOutOfBoundsException();
if (len == 0)
return;
if (leftover != 0) {
if (leftover == 1) {
b1 = b[off++] & 0xff;
len--;
if (len == 0) {
leftover++;
return;
}
}
b2 = b[off++] & 0xff;
len--;
checkNewline();
out.write(base64[b0 >> 2]);
out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]);
out.write(base64[b2 & 0x3f]);
linepos += 4;
}
int nBits24 = len / 3;
leftover = len - (nBits24 * 3);
while (nBits24-- > 0) {
checkNewline();
int bits = (b[off++] & 0xff) << 16 |
(b[off++] & 0xff) << 8 |
(b[off++] & 0xff);
out.write(base64[(bits >>> 18) & 0x3f]);
out.write(base64[(bits >>> 12) & 0x3f]);
out.write(base64[(bits >>> 6) & 0x3f]);
out.write(base64[bits & 0x3f]);
linepos += 4;
}
if (leftover == 1) {
b0 = b[off++] & 0xff;
} else if (leftover == 2) {
b0 = b[off++] & 0xff;
b1 = b[off++] & 0xff;
}
}
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
if (leftover == 1) {
checkNewline();
out.write(base64[b0 >> 2]);
out.write(base64[(b0 << 4) & 0x3f]);
out.write('=');
out.write('=');
} else if (leftover == 2) {
checkNewline();
out.write(base64[b0 >> 2]);
out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
out.write(base64[(b1 << 2) & 0x3f]);
out.write('=');
}
leftover = 0;
out.close();
}
}
}
/*
* An input stream for decoding Base64 bytes
*/
private static class DecInputStream extends InputStream {
private final InputStream is;
private final boolean isMIME;
private final int[] base64; // base64 -> byte mapping
private int bits = 0; // 24-bit buffer for decoding
private int nextin = 18; // next available "off" in "bits" for input;
// -> 18, 12, 6, 0
private int nextout = -8; // next available "off" in "bits" for output;
// -> 8, 0, -8 (no byte for output)
private boolean eof = false;
private boolean closed = false;
DecInputStream(InputStream is, int[] base64, boolean isMIME) {
this.is = is;
this.base64 = base64;
this.isMIME = isMIME;
}
private byte[] sbBuf = new byte[1];
@Override
public int read() throws IOException {
return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (closed)
throw new IOException("Stream is closed");
if (eof && nextout < 0) // eof and no leftover
return -1;
if (off < 0 || len < 0 || len > b.length - off)
throw new IndexOutOfBoundsException();
int oldOff = off;
if (nextout >= 0) { // leftover output byte(s) in bits buf
do {
if (len == 0)
return off - oldOff;
b[off++] = (byte)(bits >> nextout);
len--;
nextout -= 8;
} while (nextout >= 0);
bits = 0;
}
while (len > 0) {
int v = is.read();
if (v == -1) {
eof = true;
if (nextin != 18)
throw new IOException("Base64 stream has un-decoded dangling byte(s).");
if (off == oldOff)
return -1;
else
return off - oldOff;
}
if (v == '=') { // padding byte(s)
if (nextin != 6 && nextin != 0) {
throw new IOException("Illegal base64 ending sequence:" + nextin);
}
b[off++] = (byte)(bits >> (16));
len--;
if (nextin == 0) { // only one padding byte
if (len == 0) { // no enough output space
bits >>= 8; // shift to lowest byte
nextout = 0;
} else {
b[off++] = (byte) (bits >> 8);
}
}
eof = true;
break;
}
if ((v = base64[v]) == -1) {
if (isMIME) // skip if for rfc2045
continue;
else
throw new IOException("Illegal base64 character " +
Integer.toString(v, 16));
}
bits |= (v << nextin);
if (nextin == 0) {
nextin = 18; // clear for next
nextout = 16;
while (nextout >= 0) {
b[off++] = (byte)(bits >> nextout);
len--;
nextout -= 8;
if (len == 0 && nextout >= 0) { // don't clean "bits"
return off - oldOff;
}
}
bits = 0;
} else {
nextin -= 6;
}
}
return off - oldOff;
}
@Override
public int available() throws IOException {
if (closed)
throw new IOException("Stream is closed");
return is.available(); // TBD:
}
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
is.close();
}
}
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test 4235519
* @summary tests java.util.Base64
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Base64;
import java.util.Random;
public class TestBase64 {
public static void main(String args[]) throws Throwable {
int numRuns = 10;
int numBytes = 200;
if (args.length > 1) {
numRuns = Integer.parseInt(args[0]);
numBytes = Integer.parseInt(args[1]);
}
test(Base64.getEncoder(), Base64.getDecoder(),
numRuns, numBytes);
test(Base64.getUrlEncoder(), Base64.getUrlDecoder(),
numRuns, numBytes);
test(Base64.getMimeEncoder(), Base64.getMimeDecoder(),
numRuns, numBytes);
Random rnd = new java.util.Random();
byte[] nl_1 = new byte[] {'\n'};
byte[] nl_2 = new byte[] {'\n', '\r'};
byte[] nl_3 = new byte[] {'\n', '\r', '\n'};
for (int i = 0; i < 10; i++) {
int len = rnd.nextInt(200) + 4;
test(Base64.getEncoder(len, nl_1),
Base64.getMimeDecoder(),
numRuns, numBytes);
test(Base64.getEncoder(len, nl_2),
Base64.getMimeDecoder(),
numRuns, numBytes);
test(Base64.getEncoder(len, nl_3),
Base64.getMimeDecoder(),
numRuns, numBytes);
}
testNull(Base64.getEncoder());
testNull(Base64.getUrlEncoder());
testNull(Base64.getMimeEncoder());
testNull(Base64.getEncoder(10, new byte[]{'\n'}));
testNull(Base64.getDecoder());
testNull(Base64.getUrlDecoder());
testNull(Base64.getMimeDecoder());
checkNull(new Runnable() { public void run() { Base64.getEncoder(10, null); }});
testIOE(Base64.getEncoder());
testIOE(Base64.getUrlEncoder());
testIOE(Base64.getMimeEncoder());
testIOE(Base64.getEncoder(10, new byte[]{'\n'}));
byte[] src = new byte[1024];
new Random().nextBytes(src);
final byte[] decoded = Base64.getEncoder().encode(src);
testIOE(Base64.getDecoder(), decoded);
testIOE(Base64.getMimeDecoder(), decoded);
testIOE(Base64.getUrlDecoder(), Base64.getUrlEncoder().encode(src));
// illegal line separator
checkIAE(new Runnable() { public void run() { Base64.getEncoder(10, new byte[]{'\r', 'N'}); }});
// illegal base64 character
decoded[2] = (byte)0xe0;
checkIAE(new Runnable() {
public void run() { Base64.getDecoder().decode(decoded); }});
checkIAE(new Runnable() {
public void run() { Base64.getDecoder().decode(decoded, new byte[1024]); }});
checkIAE(new Runnable() { public void run() {
Base64.getDecoder().decode(ByteBuffer.wrap(decoded)); }});
checkIAE(new Runnable() { public void run() {
Base64.getDecoder().decode(ByteBuffer.wrap(decoded), ByteBuffer.allocate(1024)); }});
checkIAE(new Runnable() { public void run() {
Base64.getDecoder().decode(ByteBuffer.wrap(decoded), ByteBuffer.allocateDirect(1024)); }});
}
private static sun.misc.BASE64Encoder sunmisc = new sun.misc.BASE64Encoder();
private static void test(Base64.Encoder enc, Base64.Decoder dec,
int numRuns, int numBytes) throws Throwable {
Random rnd = new java.util.Random();
enc.encode(new byte[0]);
dec.decode(new byte[0]);
for (int i=0; i<numRuns; i++) {
for (int j=1; j<numBytes; j++) {
byte[] orig = new byte[j];
rnd.nextBytes(orig);
// --------testing encode/decode(byte[])--------
byte[] encoded = enc.encode(orig);
byte[] decoded = dec.decode(encoded);
checkEqual(orig, decoded,
"Base64 array encoding/decoding failed!");
// compare to sun.misc.BASE64Encoder
byte[] encoded2 = sunmisc.encode(orig).getBytes("ASCII");
checkEqual(normalize(encoded),
normalize(encoded2),
"Base64 enc.encode() does not match sun.misc.base64!");
// remove padding '=' to test non-padding decoding case
if (encoded[encoded.length -2] == '=')
encoded2 = Arrays.copyOf(encoded, encoded.length -2);
else if (encoded[encoded.length -1] == '=')
encoded2 = Arrays.copyOf(encoded, encoded.length -1);
else
encoded2 = null;
// --------testing encodetoString(byte[])/decode(String)--------
String str = enc.encodeToString(orig);
if (!Arrays.equals(str.getBytes("ASCII"), encoded)) {
throw new RuntimeException(
"Base64 encodingToString() failed!");
}
byte[] buf = dec.decode(new String(encoded, "ASCII"));
checkEqual(buf, orig, "Base64 decoding(String) failed!");
if (encoded2 != null) {
buf = dec.decode(new String(encoded2, "ASCII"));
checkEqual(buf, orig, "Base64 decoding(String) failed!");
}
//-------- testing encode/decode(Buffer)--------
testEncode(enc, ByteBuffer.wrap(orig), encoded);
ByteBuffer bin = ByteBuffer.allocateDirect(orig.length);
bin.put(orig).flip();
testEncode(enc, bin, encoded);
testDecode(dec, ByteBuffer.wrap(encoded), orig);
bin = ByteBuffer.allocateDirect(encoded.length);
bin.put(encoded).flip();
testDecode(dec, bin, orig);
if (encoded2 != null)
testDecode(dec, ByteBuffer.wrap(encoded2), orig);
// -------- testing encode(Buffer, Buffer)--------
testEncode(enc, encoded,
ByteBuffer.wrap(orig),
ByteBuffer.allocate(encoded.length + 10));
testEncode(enc, encoded,
ByteBuffer.wrap(orig),
ByteBuffer.allocateDirect(encoded.length + 10));
// --------testing decode(Buffer, Buffer);--------
testDecode(dec, orig,
ByteBuffer.wrap(encoded),
ByteBuffer.allocate(orig.length + 10));
testDecode(dec, orig,
ByteBuffer.wrap(encoded),
ByteBuffer.allocateDirect(orig.length + 10));
// --------testing decode.wrap(input stream)--------
// 1) random buf length
ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
InputStream is = dec.wrap(bais);
buf = new byte[orig.length + 10];
int len = orig.length;
int off = 0;
while (true) {
int n = rnd.nextInt(len);
if (n == 0)
n = 1;
n = is.read(buf, off, n);
if (n == -1) {
checkEqual(off, orig.length,
"Base64 stream decoding failed");
break;
}
off += n;
len -= n;
if (len == 0)
break;
}
buf = Arrays.copyOf(buf, off);
checkEqual(buf, orig, "Base64 stream decoding failed!");
// 2) read one byte each
bais.reset();
is = dec.wrap(bais);
buf = new byte[orig.length + 10];
off = 0;
int b;
while ((b = is.read()) != -1) {
buf[off++] = (byte)b;
}
buf = Arrays.copyOf(buf, off);
checkEqual(buf, orig, "Base64 stream decoding failed!");
// --------testing encode.wrap(output stream)--------
ByteArrayOutputStream baos = new ByteArrayOutputStream((orig.length + 2) / 3 * 4 + 10);
OutputStream os = enc.wrap(baos);
off = 0;
len = orig.length;
for (int k = 0; k < 5; k++) {
if (len == 0)
break;
int n = rnd.nextInt(len);
if (n == 0)
n = 1;
os.write(orig, off, n);
off += n;
len -= n;
}
if (len != 0)
os.write(orig, off, len);
os.close();
buf = baos.toByteArray();
checkEqual(buf, encoded, "Base64 stream encoding failed!");
// 2) write one byte each
baos.reset();
os = enc.wrap(baos);
off = 0;
while (off < orig.length) {
os.write(orig[off++]);
}
os.close();
buf = baos.toByteArray();
checkEqual(buf, encoded, "Base64 stream encoding failed!");
// --------testing encode(in, out); -> bigger buf--------
buf = new byte[encoded.length + rnd.nextInt(100)];
int ret = enc.encode(orig, buf);
checkEqual(ret, encoded.length,
"Base64 enc.encode(src, null) returns wrong size!");
buf = Arrays.copyOf(buf, ret);
checkEqual(buf, encoded,
"Base64 enc.encode(src, dst) failed!");
// --------testing decode(in, out); -> bigger buf--------
buf = new byte[orig.length + rnd.nextInt(100)];
ret = dec.decode(encoded, buf);
checkEqual(ret, orig.length,
"Base64 enc.encode(src, null) returns wrong size!");
buf = Arrays.copyOf(buf, ret);
checkEqual(buf, orig,
"Base64 dec.decode(src, dst) failed!");
}
}
}
private static final byte[] ba_null = null;
private static final String str_null = null;
private static final ByteBuffer bb_null = null;
private static void testNull(final Base64.Encoder enc) {
checkNull(new Runnable() { public void run() { enc.encode(ba_null); }});
checkNull(new Runnable() { public void run() { enc.encodeToString(ba_null); }});
checkNull(new Runnable() { public void run() { enc.encode(ba_null, new byte[10]); }});
checkNull(new Runnable() { public void run() { enc.encode(new byte[10], ba_null); }});
checkNull(new Runnable() { public void run() { enc.encode(bb_null); }});
checkNull(new Runnable() { public void run() { enc.encode(bb_null, ByteBuffer.allocate(10), 0); }});
checkNull(new Runnable() { public void run() { enc.encode(ByteBuffer.allocate(10), bb_null, 0); }});
}
private static void testNull(final Base64.Decoder dec) {
checkNull(new Runnable() { public void run() { dec.decode(ba_null); }});
checkNull(new Runnable() { public void run() { dec.decode(str_null); }});
checkNull(new Runnable() { public void run() { dec.decode(ba_null, new byte[10]); }});
checkNull(new Runnable() { public void run() { dec.decode(new byte[10], ba_null); }});
checkNull(new Runnable() { public void run() { dec.decode(bb_null); }});
checkNull(new Runnable() { public void run() { dec.decode(bb_null, ByteBuffer.allocate(10)); }});
checkNull(new Runnable() { public void run() { dec.decode(ByteBuffer.allocate(10), bb_null); }});
}
private static interface Testable {
public void test() throws Throwable;
}
private static void testIOE(final Base64.Encoder enc) throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
final OutputStream os = enc.wrap(baos);
os.write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9});
os.close();
checkIOE(new Testable() { public void test() throws Throwable { os.write(10); }});
checkIOE(new Testable() { public void test() throws Throwable { os.write(new byte[] {10}); }});
checkIOE(new Testable() { public void test() throws Throwable { os.write(new byte[] {10}, 1, 4); }});
}
private static void testIOE(final Base64.Decoder dec, byte[] decoded) throws Throwable {
ByteArrayInputStream bais = new ByteArrayInputStream(decoded);
final InputStream is = dec.wrap(bais);
is.read(new byte[10]);
is.close();
checkIOE(new Testable() { public void test() throws Throwable { is.read(); }});
checkIOE(new Testable() { public void test() throws Throwable { is.read(new byte[] {10}); }});
checkIOE(new Testable() { public void test() throws Throwable { is.read(new byte[] {10}, 1, 4); }});
checkIOE(new Testable() { public void test() throws Throwable { is.available(); }});
checkIOE(new Testable() { public void test() throws Throwable { is.skip(20); }});
}
private static final void checkNull(Runnable r) {
try {
r.run();
throw new RuntimeException("NPE is not thrown as expected");
} catch (NullPointerException npe) {}
}
private static final void checkIOE(Testable t) throws Throwable {
try {
t.test();
throw new RuntimeException("IOE is not thrown as expected");
} catch (IOException ioe) {}
}
private static final void checkIAE(Runnable r) throws Throwable {
try {
r.run();
throw new RuntimeException("IAE is not thrown as expected");
} catch (IllegalArgumentException iae) {}
}
private static final void testEncode(Base64.Encoder enc, ByteBuffer bin, byte[] expected)
throws Throwable {
ByteBuffer bout = enc.encode(bin);
byte[] buf = new byte[bout.remaining()];
bout.get(buf);
if (bin.hasRemaining()) {
throw new RuntimeException(
"Base64 enc.encode(ByteBuffer) failed!");
}
checkEqual(buf, expected, "Base64 enc.encode(bf, bf) failed!");
}
private static final void testDecode(Base64.Decoder dec, ByteBuffer bin, byte[] expected)
throws Throwable {
ByteBuffer bout = dec.decode(bin);
byte[] buf = new byte[bout.remaining()];
bout.get(buf);
checkEqual(buf, expected, "Base64 dec.decode(bf) failed!");
}
private static final void testEncode(Base64.Encoder enc, byte[] expected,
ByteBuffer ibb, ByteBuffer obb)
throws Throwable {
Random rnd = new Random();
int bytesOut = enc.encode(ibb, obb, 0);
if (ibb.hasRemaining()) {
throw new RuntimeException(
"Base64 enc.encode(bf, bf) failed with wrong return!");
}
obb.flip();
byte[] buf = new byte[obb.remaining()];
obb.get(buf);
checkEqual(buf, expected, "Base64 enc.encode(bf, bf) failed!");
ibb.rewind();
obb.position(0);
obb.limit(0);
bytesOut = 0;
do { // increase the "limit" incrementally & randomly
int n = rnd.nextInt(expected.length - obb.position());
if (n == 0)
n = 1;
obb.limit(obb.limit() + n);
//obb.limit(Math.min(obb.limit() + n, expected.length));
bytesOut = enc.encode(ibb, obb, bytesOut);
} while (ibb.hasRemaining());
obb.flip();
buf = new byte[obb.remaining()];
obb.get(buf);
checkEqual(buf, expected, "Base64 enc.encode(bf, bf) failed!");
}
private static final void testDecode(Base64.Decoder dec, byte[] expected,
ByteBuffer ibb, ByteBuffer obb)
throws Throwable {
Random rnd = new Random();
dec.decode(ibb, obb);
if (ibb.hasRemaining()) {
throw new RuntimeException(
"Base64 dec.decode(bf, bf) failed with un-decoded ibb!");
}
obb.flip();
byte[] buf = new byte[obb.remaining()];
obb.get(buf);
checkEqual(buf, expected, "Base64 dec.decode(bf, bf) failed!");
ibb.rewind();
obb.position(0);
obb.limit(0);
do { // increase the "limit" incrementally & randomly
int n = rnd.nextInt(expected.length - obb.position());
if (n == 0)
n = 1;
obb.limit(obb.limit() + n);
dec.decode(ibb, obb);
} while (ibb.hasRemaining());
obb.flip();
buf = new byte[obb.remaining()];
obb.get(buf);
checkEqual(buf, expected, "Base64 dec.decode(bf, bf) failed!");
}
private static final void checkEqual(int v1, int v2, String msg)
throws Throwable {
if (v1 != v2) {
System.out.printf(" v1=%d%n", v1);
System.out.printf(" v2=%d%n", v2);
throw new RuntimeException(msg);
}
}
private static final void checkEqual(byte[] r1, byte[] r2, String msg)
throws Throwable {
if (!Arrays.equals(r1, r2)) {
System.out.printf(" r1[%d]=[%s]%n", r1.length, new String(r1));
System.out.printf(" r2[%d]=[%s]%n", r2.length, new String(r2));
throw new RuntimeException(msg);
}
}
// remove line feeds,
private static final byte[] normalize(byte[] src) {
int n = 0;
boolean hasUrl = false;
for (int i = 0; i < src.length; i++) {
if (src[i] == '\r' || src[i] == '\n')
n++;
if (src[i] == '-' || src[i] == '_')
hasUrl = true;
}
if (n == 0 && hasUrl == false)
return src;
byte[] ret = new byte[src.length - n];
int j = 0;
for (int i = 0; i < src.length; i++) {
if (src[i] == '-')
ret[j++] = '+';
else if (src[i] == '_')
ret[j++] = '/';
else if (src[i] != '\r' && src[i] != '\n')
ret[j++] = src[i];
}
return ret;
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test 4235519
* @author Eric Wang <yiming.wang@oracle.com>
* @summary tests java.util.Base64
*/
import java.io.BufferedReader;
import java.io.FileReader;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.Objects;
import java.util.Random;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class TestBase64Golden {
public static void main(String[] args) throws Exception {
test0(Base64Type.BASIC, Base64.getEncoder(), Base64.getDecoder(),
"plain.txt", "baseEncode.txt");
test0(Base64Type.URLSAFE, Base64.getUrlEncoder(), Base64.getUrlDecoder(),
"plain.txt", "urlEncode.txt");
test0(Base64Type.MIME, Base64.getMimeEncoder(), Base64.getMimeDecoder(),
"plain.txt", "mimeEncode.txt");
test1();
test2();
}
public static void test0(Base64Type type, Encoder encoder, Decoder decoder,
String srcFile, String encodedFile) throws Exception {
String[] srcLns = Files.readAllLines(Paths.get(SRCDIR, srcFile), DEF_CHARSET)
.toArray(new String[0]);
String[] encodedLns = Files.readAllLines(Paths.get(SRCDIR, encodedFile),
DEF_CHARSET)
.toArray(new String[0]);
int lns = 0;
for (String srcStr : srcLns) {
String encodedStr = null;
if (type != Base64Type.MIME) {
encodedStr = encodedLns[lns++];
} else {
while (lns < encodedLns.length) {
String s = encodedLns[lns++];
if (s.length() == 0)
break;
if (encodedStr != null) {
encodedStr += DEFAULT_CRLF + s;
} else {
encodedStr = s;
}
}
if (encodedStr == null && srcStr.length() == 0) {
encodedStr = "";
}
}
System.out.printf("%n src[%d]: %s%n", srcStr.length(), srcStr);
System.out.printf("encoded[%d]: %s%n", encodedStr.length(), encodedStr);
byte[] srcArr = srcStr.getBytes(DEF_CHARSET);
byte[] encodedArr = encodedStr.getBytes(DEF_CHARSET);
ByteBuffer srcBuf = ByteBuffer.wrap(srcArr);
ByteBuffer encodedBuf = ByteBuffer.wrap(encodedArr);
byte[] resArr = new byte[encodedArr.length];
// test int encode(byte[], byte[])
int len = encoder.encode(srcArr, resArr);
assertEqual(len, encodedArr.length);
assertEqual(resArr, encodedArr);
// test byte[] encode(byte[])
resArr = encoder.encode(srcArr);
assertEqual(resArr, encodedArr);
// test ByteBuffer encode(ByteBuffer)
int limit = srcBuf.limit();
ByteBuffer resBuf = encoder.encode(srcBuf);
assertEqual(srcBuf.position(), limit);
assertEqual(srcBuf.limit(), limit);
assertEqual(resBuf, encodedBuf);
srcBuf.rewind(); // reset for next test
// test encode(ByteBuffer, ByteBuffer, bytesOut)
resBuf.clear();
len = encoder.encode(srcBuf, resBuf, 0);
assertEqual(len, encodedArr.length);
assertEqual(srcBuf.position(), limit);
assertEqual(srcBuf.limit(), limit);
assertEqual(resBuf.position(), len);
resBuf.flip();
assertEqual(resBuf, encodedBuf);
srcBuf.rewind();
// test encode(ByteBuffer, ByteBuffer, bytesOut)[direct]
ByteBuffer resBuf_d = ByteBuffer.allocateDirect(encodedArr.length);
len = encoder.encode(srcBuf, resBuf_d, 0);
assertEqual(len, encodedArr.length);
assertEqual(srcBuf.position(), limit);
assertEqual(srcBuf.limit(), limit);
assertEqual(resBuf_d.position(), len);
resBuf_d.flip();
assertEqual(resBuf_d, encodedBuf);
srcBuf.rewind();
// test String encodeToString(byte[])
String resEncodeStr = encoder.encodeToString(srcArr);
assertEqual(resEncodeStr, encodedStr);
// test int decode(byte[], byte[])
resArr = new byte[srcArr.length];
len = decoder.decode(encodedArr, resArr);
assertEqual(len, srcArr.length);
assertEqual(resArr, srcArr);
// test byte[] decode(byte[])
resArr = decoder.decode(encodedArr);
assertEqual(resArr, srcArr);
// test ByteBuffer decode(ByteBuffer)
limit = encodedBuf.limit();
resBuf = decoder.decode(encodedBuf);
assertEqual(encodedBuf.position(), limit);
assertEqual(encodedBuf.limit(), limit);
assertEqual(resBuf, srcBuf);
encodedBuf.rewind(); // reset for next test
// test int decode(ByteBuffer, ByteBuffer)
resBuf.clear();
len = decoder.decode(encodedBuf, resBuf);
assertEqual(len, srcArr.length);
assertEqual(encodedBuf.position(), limit);
assertEqual(encodedBuf.limit(), limit);
assertEqual(resBuf.position(), len);
resBuf.flip();
assertEqual(resBuf, srcBuf);
encodedBuf.rewind(); // reset for next test
// test int decode(ByteBuffer, ByteBuffer)[direct]
resBuf_d = ByteBuffer.allocateDirect(srcArr.length);
len = decoder.decode(encodedBuf, resBuf_d);
assertEqual(len, srcArr.length);
assertEqual(encodedBuf.position(), limit);
assertEqual(encodedBuf.limit(), limit);
assertEqual(resBuf_d.position(), len);
resBuf_d.flip();
assertEqual(resBuf_d, srcBuf);
encodedBuf.rewind(); // reset for next test
// test byte[] decode(String)
resArr = decoder.decode(encodedStr);
assertEqual(resArr, srcArr);
// test compatible with sun.misc.Base64Encoder
if (type == Base64Type.MIME) {
sun.misc.BASE64Encoder miscEncoder = new BASE64Encoder();
sun.misc.BASE64Decoder miscDecoder = new BASE64Decoder();
resArr = decoder.decode(miscEncoder.encode(srcArr));
assertEqual(resArr, srcArr);
resArr = encoder.encode(miscDecoder.decodeBuffer(encodedStr));
assertEqual(new String(resArr, DEF_CHARSET), encodedStr);
}
}
}
private static void test1() throws Exception {
byte[] src = new byte[6];
new Random().nextBytes(src);
ByteBuffer srcBuf = ByteBuffer.allocate(10);
srcBuf.position(2);
srcBuf.mark();
srcBuf.limit(8);
srcBuf.put(src);
srcBuf.reset();
ByteBuffer dstBuf = ByteBuffer.allocate((src.length + 2) / 3 * 4);
Base64.getEncoder().encode(srcBuf, dstBuf, 0);
dstBuf.rewind();
byte[] dst = new byte[dstBuf.limit()];
dstBuf.get(dst);
System.out.printf("%n src[%d]: %s%n", src.length, new String(src));
System.out.printf("encoded[%d]: %s%n", dst.length, new String(dst));
assertEqual(src, Base64.getDecoder().decode(dst));
dstBuf = ByteBuffer.allocateDirect((src.length + 2) / 3 * 4);
srcBuf.reset();
Base64.getEncoder().encode(srcBuf, dstBuf, 0);
dstBuf.rewind();
dst = new byte[dstBuf.limit()];
dstBuf.get(dst);
assertEqual(src, Base64.getDecoder().decode(dst));
}
private static void test2() throws Exception {
byte[] src = new byte[] {
46, -97, -35, -44, 127, -60, -39, -4, -112, 34, -57, 47, -14, 67,
40, 18, 90, -59, 68, 112, 23, 121, -91, 94, 35, 49, 104, 17, 30,
-80, -104, -3, -53, 27, 38, -72, -47, 113, -52, 18, 5, -126 };
Encoder encoder = Base64.getEncoder(49, new byte[] { 0x7e });
byte[] encoded = encoder.encode(src);
Decoder decoder = Base64.getMimeDecoder();
byte[] decoded = decoder.decode(encoded);
if (!Objects.deepEquals(src, decoded)) {
throw new RuntimeException();
}
}
// helper
enum Base64Type {
BASIC, URLSAFE, MIME
}
private static final String SRCDIR = System.getProperty("test.src", ".");
private static final Charset DEF_CHARSET = StandardCharsets.US_ASCII;
private static final String DEF_EXCEPTION_MSG =
"Assertion failed! The result is not same as expected";
private static final String DEFAULT_CRLF = "\r\n";
private static void assertEqual(Object result, Object expect) {
if (!Objects.deepEquals(result, expect)) {
String resultStr = result.toString();
String expectStr = expect.toString();
if (result instanceof byte[]) {
resultStr = new String((byte[]) result, DEF_CHARSET);
}
if (expect instanceof byte[]) {
expectStr = new String((byte[]) expect, DEF_CHARSET);
}
throw new RuntimeException(DEF_EXCEPTION_MSG +
" result: " + resultStr + " expected: " + expectStr);
}
}
}
VGhpcyB0ZXN0IGRhdGEgaXMgcGFydCBvZiByZmMyMDQ1IHdoaWNoIGluY2x1ZGVzIGFsbCBjaGFyYWN0ZXJzIGF+eiBBflosIDB+OSBhbmQgYWxsIHN5bWJvbHMs
SXQgaXMgdXNlZCB0byB0ZXN0IGphdmEudXRpbC5CYXNlNjQuRW5jb2RlciwgYW5kIHdpbGwgYmUgZW5jb2RlZCBieSBvcmcuYXBhY2hlLmNvbW1vbnMuY29kZWMuYmluYXJ5LkJhc2U2NC5qYXZh
dG8gdGVzdCBqYXZhLnV0aWwuQmFzZTY0LkRlY29kZXI7
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgIFtQYWdlIDFd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
ICAgVGhlc2UgZG9jdW1lbnRzIGFyZSByZXZpc2lvbnMgb2YgUkZDcyAxNTIxLCAxNTIyLCBhbmQgMTU5MCwgd2hpY2g=
ICAgdGhlbXNlbHZlcyB3ZXJlIHJldmlzaW9ucyBvZiBSRkNzIDEzNDEgYW5kIDEzNDIuICBBbiBhcHBlbmRpeCBpbiBSRkM=
ICAgMjA0OSBkZXNjcmliZXMgZGlmZmVyZW5jZXMgYW5kIGNoYW5nZXMgZnJvbSBwcmV2aW91cyB2ZXJzaW9ucy4=
VGFibGUgb2YgQ29udGVudHM=
ICAgMS4gSW50cm9kdWN0aW9uIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgIDM=
ICAgMi4gRGVmaW5pdGlvbnMsIENvbnZlbnRpb25zLCBhbmQgR2VuZXJpYyBCTkYgR3JhbW1hciAuLi4uICAgIDU=
ICAgMy4gTUlNRSBIZWFkZXIgRmllbGRzIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgIDg=
ICAgNC4gTUlNRS1WZXJzaW9uIEhlYWRlciBGaWVsZCAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgIDg=
ICAgNS4gQ29udGVudC1UeXBlIEhlYWRlciBGaWVsZCAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMTA=
ICAgNi4gQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZyBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uICAgMTQ=
ICAgNy4gQ29udGVudC1JRCBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjY=
ICAgOC4gQ29udGVudC1EZXNjcmlwdGlvbiBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgOS4gQWRkaXRpb25hbCBNSU1FIEhlYWRlciBGaWVsZHMgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgMTAuIFN1bW1hcnkgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgMTEuIFNlY3VyaXR5IENvbnNpZGVyYXRpb25zIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgMTIuIEF1dGhvcnMnIEFkZHJlc3NlcyAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjg=
ICAgQS4gQ29sbGVjdGVkIEdyYW1tYXIgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjk=
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgIFtQYWdlIDdd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
My4gIE1JTUUgSGVhZGVyIEZpZWxkcw==
ICAgTUlNRSBkZWZpbmVzIGEgbnVtYmVyIG9mIG5ldyBSRkMgODIyIGhlYWRlciBmaWVsZHMgdGhhdCBhcmUgdXNlZCB0bw==
ICAgZGVzY3JpYmUgdGhlIGNvbnRlbnQgb2YgYSBNSU1FIGVudGl0eS4gIFRoZXNlIGhlYWRlciBmaWVsZHMgb2NjdXIgaW4=
ICAgYXQgbGVhc3QgdHdvIGNvbnRleHRzOg==
ICAgICgxKSAgIEFzIHBhcnQgb2YgYSByZWd1bGFyIFJGQyA4MjIgbWVzc2FnZSBoZWFkZXIu
ICAgICgyKSAgIEluIGEgTUlNRSBib2R5IHBhcnQgaGVhZGVyIHdpdGhpbiBhIG11bHRpcGFydA==
ICAgICAgICAgIGNvbnN0cnVjdC4=
ICAgVGhlIGZvcm1hbCBkZWZpbml0aW9uIG9mIHRoZXNlIGhlYWRlciBmaWVsZHMgaXMgYXMgZm9sbG93czo=
ICAgICBNSU1FLW1lc3NhZ2UtaGVhZGVycyA6PSBlbnRpdHktaGVhZGVycw==
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZHM=
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uIENSTEY=
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IFRoZSBvcmRlcmluZyBvZiB0aGUgaGVhZGVy
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IGZpZWxkcyBpbXBsaWVkIGJ5IHRoaXMgQk5G
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IGRlZmluaXRpb24gc2hvdWxkIGJlIGlnbm9yZWQu
ICAgICBNSU1FLXBhcnQtaGVhZGVycyA6PSBlbnRpdHktaGVhZGVycw==
ICAgICAgICAgICAgICAgICAgICAgICAgICBbIGZpZWxkcyBd
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IEFueSBmaWVsZCBub3QgYmVnaW5uaW5nIHdpdGg=
ICAgICAgICAgICAgICAgICAgICAgICAgICA7ICJjb250ZW50LSIgY2FuIGhhdmUgbm8gZGVmaW5lZA==
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IG1lYW5pbmcgYW5kIG1heSBiZSBpZ25vcmVkLg==
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IFRoZSBvcmRlcmluZyBvZiB0aGUgaGVhZGVy
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IGZpZWxkcyBpbXBsaWVkIGJ5IHRoaXMgQk5G
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IGRlZmluaXRpb24gc2hvdWxkIGJlIGlnbm9yZWQu
ICAgVGhlIHN5bnRheCBvZiB0aGUgdmFyaW91cyBzcGVjaWZpYyBNSU1FIGhlYWRlciBmaWVsZHMgd2lsbCBiZQ==
ICAgZGVzY3JpYmVkIGluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMu
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgW1BhZ2UgMTFd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
NS4xLiAgU3ludGF4IG9mIHRoZSBDb250ZW50LVR5cGUgSGVhZGVyIEZpZWxk
ICAgSW4gdGhlIEF1Z21lbnRlZCBCTkYgbm90YXRpb24gb2YgUkZDIDgyMiwgYSBDb250ZW50LVR5cGUgaGVhZGVyIGZpZWxk
ICAgdmFsdWUgaXMgZGVmaW5lZCBhcyBmb2xsb3dzOg==
ICAgICBjb250ZW50IDo9ICJDb250ZW50LVR5cGUiICI6IiB0eXBlICIvIiBzdWJ0eXBl
ICAgICAgICAgICAgICAgICooIjsiIHBhcmFtZXRlcik=
ICAgICAgICAgICAgICAgIDsgTWF0Y2hpbmcgb2YgbWVkaWEgdHlwZSBhbmQgc3VidHlwZQ==
ICAgICAgICAgICAgICAgIDsgaXMgQUxXQVlTIGNhc2UtaW5zZW5zaXRpdmUu
ICAgICB0eXBlIDo9IGRpc2NyZXRlLXR5cGUgLyBjb21wb3NpdGUtdHlwZQ==
ICAgICBkaXNjcmV0ZS10eXBlIDo9ICJ0ZXh0IiAvICJpbWFnZSIgLyAiYXVkaW8iIC8gInZpZGVvIiAv
ICAgICAgICAgICAgICAgICAgICAgICJhcHBsaWNhdGlvbiIgLyBleHRlbnNpb24tdG9rZW4=
ICAgICBjb21wb3NpdGUtdHlwZSA6PSAibWVzc2FnZSIgLyAibXVsdGlwYXJ0IiAvIGV4dGVuc2lvbi10b2tlbg==
ICAgICBleHRlbnNpb24tdG9rZW4gOj0gaWV0Zi10b2tlbiAvIHgtdG9rZW4=
ICAgICBpZXRmLXRva2VuIDo9IDxBbiBleHRlbnNpb24gdG9rZW4gZGVmaW5lZCBieSBh
ICAgICAgICAgICAgICAgICAgICBzdGFuZGFyZHMtdHJhY2sgUkZDIGFuZCByZWdpc3RlcmVk
ICAgICAgICAgICAgICAgICAgICB3aXRoIElBTkEuPg==
ICAgICB4LXRva2VuIDo9IDxUaGUgdHdvIGNoYXJhY3RlcnMgIlgtIiBvciAieC0iIGZvbGxvd2VkLCB3aXRo
ICAgICAgICAgICAgICAgICBubyBpbnRlcnZlbmluZyB3aGl0ZSBzcGFjZSwgYnkgYW55IHRva2VuPg==
ICAgICBzdWJ0eXBlIDo9IGV4dGVuc2lvbi10b2tlbiAvIGlhbmEtdG9rZW4=
ICAgICBpYW5hLXRva2VuIDo9IDxBIHB1YmxpY2x5LWRlZmluZWQgZXh0ZW5zaW9uIHRva2VuLiBUb2tlbnM=
ICAgICAgICAgICAgICAgICAgICBvZiB0aGlzIGZvcm0gbXVzdCBiZSByZWdpc3RlcmVkIHdpdGggSUFOQQ==
ICAgICAgICAgICAgICAgICAgICBhcyBzcGVjaWZpZWQgaW4gUkZDIDIwNDguPg==
ICAgICBwYXJhbWV0ZXIgOj0gYXR0cmlidXRlICI9IiB2YWx1ZQ==
ICAgICBhdHRyaWJ1dGUgOj0gdG9rZW4=
ICAgICAgICAgICAgICAgICAgOyBNYXRjaGluZyBvZiBhdHRyaWJ1dGVz
ICAgICAgICAgICAgICAgICAgOyBpcyBBTFdBWVMgY2FzZS1pbnNlbnNpdGl2ZS4=
ICAgICB2YWx1ZSA6PSB0b2tlbiAvIHF1b3RlZC1zdHJpbmc=
ICAgICB0b2tlbiA6PSAxKjxhbnkgKFVTLUFTQ0lJKSBDSEFSIGV4Y2VwdCBTUEFDRSwgQ1RMcyw=
ICAgICAgICAgICAgICAgICBvciB0c3BlY2lhbHM+
ICAgICB0c3BlY2lhbHMgOj0gICIoIiAvICIpIiAvICI8IiAvICI+IiAvICJAIiAv
ICAgICAgICAgICAgICAgICAgICIsIiAvICI7IiAvICI6IiAvICJcIiAvIDwiPg==
ICAgICAgICAgICAgICAgICAgICIvIiAvICJbIiAvICJdIiAvICI/IiAvICI9Ig==
ICAgICAgICAgICAgICAgICAgIDsgTXVzdCBiZSBpbiBxdW90ZWQtc3RyaW5nLA==
ICAgICAgICAgICAgICAgICAgIDsgdG8gdXNlIHdpdGhpbiBwYXJhbWV0ZXIgdmFsdWVz
ICAgICBkZXNjcmlwdGlvbiA6PSAiQ29udGVudC1EZXNjcmlwdGlvbiIgIjoiICp0ZXh0
ICAgICBlbmNvZGluZyA6PSAiQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZyIgIjoiIG1lY2hhbmlzbQ==
ICAgICBlbnRpdHktaGVhZGVycyA6PSBbIGNvbnRlbnQgQ1JMRiBd
ICAgICAgICAgICAgICAgICAgICBbIGVuY29kaW5nIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICBbIGlkIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICBbIGRlc2NyaXB0aW9uIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICAqKCBNSU1FLWV4dGVuc2lvbi1maWVsZCBDUkxGICk=
ICAgICBoZXgtb2N0ZXQgOj0gIj0iIDIoRElHSVQgLyAiQSIgLyAiQiIgLyAiQyIgLyAiRCIgLyAiRSIgLyAiRiIp
ICAgICAgICAgICAgICAgOyBPY3RldCBtdXN0IGJlIHVzZWQgZm9yIGNoYXJhY3RlcnMgPiAxMjcsID0s
ICAgICAgICAgICAgICAgOyBTUEFDRXMgb3IgVEFCcyBhdCB0aGUgZW5kcyBvZiBsaW5lcywgYW5kIGlz
ICAgICAgICAgICAgICAgOyByZWNvbW1lbmRlZCBmb3IgYW55IGNoYXJhY3RlciBub3QgbGlzdGVkIGlu
ICAgICAgICAgICAgICAgOyBSRkMgMjA0OSBhcyAibWFpbC1zYWZlIi4=
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
ICAgICAgICAgIG11c3QgYmUgdXNlZC4gIEFuIGVxdWFsIHNpZ24gYXMgdGhlIGxhc3QgY2hhcmFjdGVyIG9uIGE=
ICAgICAgICAgIGVuY29kZWQgbGluZSBpbmRpY2F0ZXMgc3VjaCBhIG5vbi1zaWduaWZpY2FudCAoInNvZnQiKQ==
ICAgICAgICAgIGxpbmUgYnJlYWsgaW4gdGhlIGVuY29kZWQgdGV4dC4=
ICAgVGh1cyBpZiB0aGUgInJhdyIgZm9ybSBvZiB0aGUgbGluZSBpcyBhIHNpbmdsZSB1bmVuY29kZWQgbGluZSB0aGF0
ICAgc2F5czo=
ICAgICBOb3cncyB0aGUgdGltZSBmb3IgYWxsIGZvbGsgdG8gY29tZSB0byB0aGUgYWlkIG9mIHRoZWlyIGNvdW50cnku
ICAgVGhpcyBjYW4gYmUgcmVwcmVzZW50ZWQsIGluIHRoZSBRdW90ZWQtUHJpbnRhYmxlIGVuY29kaW5nLCBhczo=
ICAgICBOb3cncyB0aGUgdGltZSA9
ICAgICBmb3IgYWxsIGZvbGsgdG8gY29tZT0=
ICAgICAgdG8gdGhlIGFpZCBvZiB0aGVpciBjb3VudHJ5Lg==
ICAgU2luY2UgdGhlIGh5cGhlbiBjaGFyYWN0ZXIgKCItIikgbWF5IGJlIHJlcHJlc2VudGVkIGFzIGl0c2VsZiBpbiB0aGU=
ICAgUXVvdGVkLVByaW50YWJsZSBlbmNvZGluZywgY2FyZSBtdXN0IGJlIHRha2VuLCB3aGVuIGVuY2Fwc3VsYXRpbmcgYQ==
ICAgcXVvdGVkLXByaW50YWJsZSBlbmNvZGVkIGJvZHkgaW5zaWRlIG9uZSBvciBtb3JlIG11bHRpcGFydCBlbnRpdGllcyw=
ICAgdG8gZW5zdXJlIHRoYXQgdGhlIGJvdW5kYXJ5IGRlbGltaXRlciBkb2VzIG5vdCBhcHBlYXIgYW55d2hlcmUgaW4gdGhl
ICAgZW5jb2RlZCBib2R5LiAgKEEgZ29vZCBzdHJhdGVneSBpcyB0byBjaG9vc2UgYSBib3VuZGFyeSB0aGF0IGluY2x1ZGVz
ICAgYSBjaGFyYWN0ZXIgc2VxdWVuY2Ugc3VjaCBhcyAiPV8iIHdoaWNoIGNhbiBuZXZlciBhcHBlYXIgaW4gYQ==
ICAgcXVvdGVkLXByaW50YWJsZSBib2R5LiAgU2VlIHRoZSBkZWZpbml0aW9uIG9mIG11bHRpcGFydCBtZXNzYWdlcyBpbg==
ICAgUkZDIDIwNDYuKQ==
ICAgICAhIiMkQFtcXV5ge3x9fiU=
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgW1BhZ2UgMjRd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
ICAgICAgICAgICAgICAgICAgICBUYWJsZSAxOiBUaGUgQmFzZTY0IEFscGhhYmV0
ICAgICBWYWx1ZSBFbmNvZGluZyAgVmFsdWUgRW5jb2RpbmcgIFZhbHVlIEVuY29kaW5nICBWYWx1ZSBFbmNvZGluZw==
ICAgICAgICAgMCBBICAgICAgICAgICAgMTcgUiAgICAgICAgICAgIDM0IGkgICAgICAgICAgICA1MSB6
ICAgICAgICAgMSBCICAgICAgICAgICAgMTggUyAgICAgICAgICAgIDM1IGogICAgICAgICAgICA1MiAw
ICAgICAgICAgMiBDICAgICAgICAgICAgMTkgVCAgICAgICAgICAgIDM2IGsgICAgICAgICAgICA1MyAx
ICAgICAgICAgMyBEICAgICAgICAgICAgMjAgVSAgICAgICAgICAgIDM3IGwgICAgICAgICAgICA1NCAy
ICAgICAgICAgNCBFICAgICAgICAgICAgMjEgViAgICAgICAgICAgIDM4IG0gICAgICAgICAgICA1NSAz
ICAgICAgICAgNSBGICAgICAgICAgICAgMjIgVyAgICAgICAgICAgIDM5IG4gICAgICAgICAgICA1NiA0
ICAgICAgICAgNiBHICAgICAgICAgICAgMjMgWCAgICAgICAgICAgIDQwIG8gICAgICAgICAgICA1NyA1
ICAgICAgICAgNyBIICAgICAgICAgICAgMjQgWSAgICAgICAgICAgIDQxIHAgICAgICAgICAgICA1OCA2
ICAgICAgICAgOCBJICAgICAgICAgICAgMjUgWiAgICAgICAgICAgIDQyIHEgICAgICAgICAgICA1OSA3
ICAgICAgICAgOSBKICAgICAgICAgICAgMjYgYSAgICAgICAgICAgIDQzIHIgICAgICAgICAgICA2MCA4
ICAgICAgICAxMCBLICAgICAgICAgICAgMjcgYiAgICAgICAgICAgIDQ0IHMgICAgICAgICAgICA2MSA5
ICAgICAgICAxMSBMICAgICAgICAgICAgMjggYyAgICAgICAgICAgIDQ1IHQgICAgICAgICAgICA2MiAr
ICAgICAgICAxMiBNICAgICAgICAgICAgMjkgZCAgICAgICAgICAgIDQ2IHUgICAgICAgICAgICA2MyAv
ICAgICAgICAxMyBOICAgICAgICAgICAgMzAgZSAgICAgICAgICAgIDQ3IHY=
ICAgICAgICAxNCBPICAgICAgICAgICAgMzEgZiAgICAgICAgICAgIDQ4IHcgICAgICAgICAocGFkKSA9
ICAgICAgICAxNSBQICAgICAgICAgICAgMzIgZyAgICAgICAgICAgIDQ5IHg=
ICAgICAgICAxNiBRICAgICAgICAgICAgMzMgaCAgICAgICAgICAgIDUwIHk=
VGhpcyB0ZXN0IGRhdGEgaXMgcGFydCBvZiByZmMyMDQ1IHdoaWNoIGluY2x1ZGVzIGFsbCBjaGFy
YWN0ZXJzIGF+eiBBflosIDB+OSBhbmQgYWxsIHN5bWJvbHMs
SXQgaXMgdXNlZCB0byB0ZXN0IGphdmEudXRpbC5CYXNlNjQuRW5jb2RlciwgYW5kIHdpbGwgYmUg
ZW5jb2RlZCBieSBvcmcuYXBhY2hlLmNvbW1vbnMuY29kZWMuYmluYXJ5LkJhc2U2NC5qYXZh
dG8gdGVzdCBqYXZhLnV0aWwuQmFzZTY0LkRlY29kZXI7
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAg
ICAgICAgIFtQYWdlIDFd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAg
ICBOb3ZlbWJlciAxOTk2
ICAgVGhlc2UgZG9jdW1lbnRzIGFyZSByZXZpc2lvbnMgb2YgUkZDcyAxNTIxLCAxNTIyLCBhbmQg
MTU5MCwgd2hpY2g=
ICAgdGhlbXNlbHZlcyB3ZXJlIHJldmlzaW9ucyBvZiBSRkNzIDEzNDEgYW5kIDEzNDIuICBBbiBh
cHBlbmRpeCBpbiBSRkM=
ICAgMjA0OSBkZXNjcmliZXMgZGlmZmVyZW5jZXMgYW5kIGNoYW5nZXMgZnJvbSBwcmV2aW91cyB2
ZXJzaW9ucy4=
VGFibGUgb2YgQ29udGVudHM=
ICAgMS4gSW50cm9kdWN0aW9uIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgIDM=
ICAgMi4gRGVmaW5pdGlvbnMsIENvbnZlbnRpb25zLCBhbmQgR2VuZXJpYyBCTkYgR3JhbW1hciAu
Li4uICAgIDU=
ICAgMy4gTUlNRSBIZWFkZXIgRmllbGRzIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgIDg=
ICAgNC4gTUlNRS1WZXJzaW9uIEhlYWRlciBGaWVsZCAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgIDg=
ICAgNS4gQ29udGVudC1UeXBlIEhlYWRlciBGaWVsZCAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMTA=
ICAgNi4gQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZyBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4u
Li4uICAgMTQ=
ICAgNy4gQ29udGVudC1JRCBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjY=
ICAgOC4gQ29udGVudC1EZXNjcmlwdGlvbiBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjc=
ICAgOS4gQWRkaXRpb25hbCBNSU1FIEhlYWRlciBGaWVsZHMgLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjc=
ICAgMTAuIFN1bW1hcnkgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjc=
ICAgMTEuIFNlY3VyaXR5IENvbnNpZGVyYXRpb25zIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjc=
ICAgMTIuIEF1dGhvcnMnIEFkZHJlc3NlcyAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjg=
ICAgQS4gQ29sbGVjdGVkIEdyYW1tYXIgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u
Li4uICAgMjk=
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAg
ICAgICAgIFtQYWdlIDdd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAg
ICBOb3ZlbWJlciAxOTk2
My4gIE1JTUUgSGVhZGVyIEZpZWxkcw==
ICAgTUlNRSBkZWZpbmVzIGEgbnVtYmVyIG9mIG5ldyBSRkMgODIyIGhlYWRlciBmaWVsZHMgdGhh
dCBhcmUgdXNlZCB0bw==
ICAgZGVzY3JpYmUgdGhlIGNvbnRlbnQgb2YgYSBNSU1FIGVudGl0eS4gIFRoZXNlIGhlYWRlciBm
aWVsZHMgb2NjdXIgaW4=
ICAgYXQgbGVhc3QgdHdvIGNvbnRleHRzOg==
ICAgICgxKSAgIEFzIHBhcnQgb2YgYSByZWd1bGFyIFJGQyA4MjIgbWVzc2FnZSBoZWFkZXIu
ICAgICgyKSAgIEluIGEgTUlNRSBib2R5IHBhcnQgaGVhZGVyIHdpdGhpbiBhIG11bHRpcGFydA==
ICAgICAgICAgIGNvbnN0cnVjdC4=
ICAgVGhlIGZvcm1hbCBkZWZpbml0aW9uIG9mIHRoZXNlIGhlYWRlciBmaWVsZHMgaXMgYXMgZm9s
bG93czo=
ICAgICBNSU1FLW1lc3NhZ2UtaGVhZGVycyA6PSBlbnRpdHktaGVhZGVycw==
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZHM=
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uIENSTEY=
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IFRoZSBvcmRlcmluZyBvZiB0aGUgaGVhZGVy
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IGZpZWxkcyBpbXBsaWVkIGJ5IHRoaXMgQk5G
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IGRlZmluaXRpb24gc2hvdWxkIGJlIGlnbm9y
ZWQu
ICAgICBNSU1FLXBhcnQtaGVhZGVycyA6PSBlbnRpdHktaGVhZGVycw==
ICAgICAgICAgICAgICAgICAgICAgICAgICBbIGZpZWxkcyBd
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IEFueSBmaWVsZCBub3QgYmVnaW5uaW5nIHdpdGg=
ICAgICAgICAgICAgICAgICAgICAgICAgICA7ICJjb250ZW50LSIgY2FuIGhhdmUgbm8gZGVmaW5l
ZA==
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IG1lYW5pbmcgYW5kIG1heSBiZSBpZ25vcmVkLg==
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IFRoZSBvcmRlcmluZyBvZiB0aGUgaGVhZGVy
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IGZpZWxkcyBpbXBsaWVkIGJ5IHRoaXMgQk5G
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IGRlZmluaXRpb24gc2hvdWxkIGJlIGlnbm9yZWQu
ICAgVGhlIHN5bnRheCBvZiB0aGUgdmFyaW91cyBzcGVjaWZpYyBNSU1FIGhlYWRlciBmaWVsZHMg
d2lsbCBiZQ==
ICAgZGVzY3JpYmVkIGluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMu
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAg
ICAgICAgW1BhZ2UgMTFd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAg
ICBOb3ZlbWJlciAxOTk2
NS4xLiAgU3ludGF4IG9mIHRoZSBDb250ZW50LVR5cGUgSGVhZGVyIEZpZWxk
ICAgSW4gdGhlIEF1Z21lbnRlZCBCTkYgbm90YXRpb24gb2YgUkZDIDgyMiwgYSBDb250ZW50LVR5
cGUgaGVhZGVyIGZpZWxk
ICAgdmFsdWUgaXMgZGVmaW5lZCBhcyBmb2xsb3dzOg==
ICAgICBjb250ZW50IDo9ICJDb250ZW50LVR5cGUiICI6IiB0eXBlICIvIiBzdWJ0eXBl
ICAgICAgICAgICAgICAgICooIjsiIHBhcmFtZXRlcik=
ICAgICAgICAgICAgICAgIDsgTWF0Y2hpbmcgb2YgbWVkaWEgdHlwZSBhbmQgc3VidHlwZQ==
ICAgICAgICAgICAgICAgIDsgaXMgQUxXQVlTIGNhc2UtaW5zZW5zaXRpdmUu
ICAgICB0eXBlIDo9IGRpc2NyZXRlLXR5cGUgLyBjb21wb3NpdGUtdHlwZQ==
ICAgICBkaXNjcmV0ZS10eXBlIDo9ICJ0ZXh0IiAvICJpbWFnZSIgLyAiYXVkaW8iIC8gInZpZGVv
IiAv
ICAgICAgICAgICAgICAgICAgICAgICJhcHBsaWNhdGlvbiIgLyBleHRlbnNpb24tdG9rZW4=
ICAgICBjb21wb3NpdGUtdHlwZSA6PSAibWVzc2FnZSIgLyAibXVsdGlwYXJ0IiAvIGV4dGVuc2lv
bi10b2tlbg==
ICAgICBleHRlbnNpb24tdG9rZW4gOj0gaWV0Zi10b2tlbiAvIHgtdG9rZW4=
ICAgICBpZXRmLXRva2VuIDo9IDxBbiBleHRlbnNpb24gdG9rZW4gZGVmaW5lZCBieSBh
ICAgICAgICAgICAgICAgICAgICBzdGFuZGFyZHMtdHJhY2sgUkZDIGFuZCByZWdpc3RlcmVk
ICAgICAgICAgICAgICAgICAgICB3aXRoIElBTkEuPg==
ICAgICB4LXRva2VuIDo9IDxUaGUgdHdvIGNoYXJhY3RlcnMgIlgtIiBvciAieC0iIGZvbGxvd2Vk
LCB3aXRo
ICAgICAgICAgICAgICAgICBubyBpbnRlcnZlbmluZyB3aGl0ZSBzcGFjZSwgYnkgYW55IHRva2Vu
Pg==
ICAgICBzdWJ0eXBlIDo9IGV4dGVuc2lvbi10b2tlbiAvIGlhbmEtdG9rZW4=
ICAgICBpYW5hLXRva2VuIDo9IDxBIHB1YmxpY2x5LWRlZmluZWQgZXh0ZW5zaW9uIHRva2VuLiBU
b2tlbnM=
ICAgICAgICAgICAgICAgICAgICBvZiB0aGlzIGZvcm0gbXVzdCBiZSByZWdpc3RlcmVkIHdpdGgg
SUFOQQ==
ICAgICAgICAgICAgICAgICAgICBhcyBzcGVjaWZpZWQgaW4gUkZDIDIwNDguPg==
ICAgICBwYXJhbWV0ZXIgOj0gYXR0cmlidXRlICI9IiB2YWx1ZQ==
ICAgICBhdHRyaWJ1dGUgOj0gdG9rZW4=
ICAgICAgICAgICAgICAgICAgOyBNYXRjaGluZyBvZiBhdHRyaWJ1dGVz
ICAgICAgICAgICAgICAgICAgOyBpcyBBTFdBWVMgY2FzZS1pbnNlbnNpdGl2ZS4=
ICAgICB2YWx1ZSA6PSB0b2tlbiAvIHF1b3RlZC1zdHJpbmc=
ICAgICB0b2tlbiA6PSAxKjxhbnkgKFVTLUFTQ0lJKSBDSEFSIGV4Y2VwdCBTUEFDRSwgQ1RMcyw=
ICAgICAgICAgICAgICAgICBvciB0c3BlY2lhbHM+
ICAgICB0c3BlY2lhbHMgOj0gICIoIiAvICIpIiAvICI8IiAvICI+IiAvICJAIiAv
ICAgICAgICAgICAgICAgICAgICIsIiAvICI7IiAvICI6IiAvICJcIiAvIDwiPg==
ICAgICAgICAgICAgICAgICAgICIvIiAvICJbIiAvICJdIiAvICI/IiAvICI9Ig==
ICAgICAgICAgICAgICAgICAgIDsgTXVzdCBiZSBpbiBxdW90ZWQtc3RyaW5nLA==
ICAgICAgICAgICAgICAgICAgIDsgdG8gdXNlIHdpdGhpbiBwYXJhbWV0ZXIgdmFsdWVz
ICAgICBkZXNjcmlwdGlvbiA6PSAiQ29udGVudC1EZXNjcmlwdGlvbiIgIjoiICp0ZXh0
ICAgICBlbmNvZGluZyA6PSAiQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZyIgIjoiIG1lY2hhbmlz
bQ==
ICAgICBlbnRpdHktaGVhZGVycyA6PSBbIGNvbnRlbnQgQ1JMRiBd
ICAgICAgICAgICAgICAgICAgICBbIGVuY29kaW5nIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICBbIGlkIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICBbIGRlc2NyaXB0aW9uIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICAqKCBNSU1FLWV4dGVuc2lvbi1maWVsZCBDUkxGICk=
ICAgICBoZXgtb2N0ZXQgOj0gIj0iIDIoRElHSVQgLyAiQSIgLyAiQiIgLyAiQyIgLyAiRCIgLyAi
RSIgLyAiRiIp
ICAgICAgICAgICAgICAgOyBPY3RldCBtdXN0IGJlIHVzZWQgZm9yIGNoYXJhY3RlcnMgPiAxMjcs
ID0s
ICAgICAgICAgICAgICAgOyBTUEFDRXMgb3IgVEFCcyBhdCB0aGUgZW5kcyBvZiBsaW5lcywgYW5k
IGlz
ICAgICAgICAgICAgICAgOyByZWNvbW1lbmRlZCBmb3IgYW55IGNoYXJhY3RlciBub3QgbGlzdGVk
IGlu
ICAgICAgICAgICAgICAgOyBSRkMgMjA0OSBhcyAibWFpbC1zYWZlIi4=
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAg
ICBOb3ZlbWJlciAxOTk2
ICAgICAgICAgIG11c3QgYmUgdXNlZC4gIEFuIGVxdWFsIHNpZ24gYXMgdGhlIGxhc3QgY2hhcmFj
dGVyIG9uIGE=
ICAgICAgICAgIGVuY29kZWQgbGluZSBpbmRpY2F0ZXMgc3VjaCBhIG5vbi1zaWduaWZpY2FudCAo
InNvZnQiKQ==
ICAgICAgICAgIGxpbmUgYnJlYWsgaW4gdGhlIGVuY29kZWQgdGV4dC4=
ICAgVGh1cyBpZiB0aGUgInJhdyIgZm9ybSBvZiB0aGUgbGluZSBpcyBhIHNpbmdsZSB1bmVuY29k
ZWQgbGluZSB0aGF0
ICAgc2F5czo=
ICAgICBOb3cncyB0aGUgdGltZSBmb3IgYWxsIGZvbGsgdG8gY29tZSB0byB0aGUgYWlkIG9mIHRo
ZWlyIGNvdW50cnku
ICAgVGhpcyBjYW4gYmUgcmVwcmVzZW50ZWQsIGluIHRoZSBRdW90ZWQtUHJpbnRhYmxlIGVuY29k
aW5nLCBhczo=
ICAgICBOb3cncyB0aGUgdGltZSA9
ICAgICBmb3IgYWxsIGZvbGsgdG8gY29tZT0=
ICAgICAgdG8gdGhlIGFpZCBvZiB0aGVpciBjb3VudHJ5Lg==
ICAgU2luY2UgdGhlIGh5cGhlbiBjaGFyYWN0ZXIgKCItIikgbWF5IGJlIHJlcHJlc2VudGVkIGFz
IGl0c2VsZiBpbiB0aGU=
ICAgUXVvdGVkLVByaW50YWJsZSBlbmNvZGluZywgY2FyZSBtdXN0IGJlIHRha2VuLCB3aGVuIGVu
Y2Fwc3VsYXRpbmcgYQ==
ICAgcXVvdGVkLXByaW50YWJsZSBlbmNvZGVkIGJvZHkgaW5zaWRlIG9uZSBvciBtb3JlIG11bHRp
cGFydCBlbnRpdGllcyw=
ICAgdG8gZW5zdXJlIHRoYXQgdGhlIGJvdW5kYXJ5IGRlbGltaXRlciBkb2VzIG5vdCBhcHBlYXIg
YW55d2hlcmUgaW4gdGhl
ICAgZW5jb2RlZCBib2R5LiAgKEEgZ29vZCBzdHJhdGVneSBpcyB0byBjaG9vc2UgYSBib3VuZGFy
eSB0aGF0IGluY2x1ZGVz
ICAgYSBjaGFyYWN0ZXIgc2VxdWVuY2Ugc3VjaCBhcyAiPV8iIHdoaWNoIGNhbiBuZXZlciBhcHBl
YXIgaW4gYQ==
ICAgcXVvdGVkLXByaW50YWJsZSBib2R5LiAgU2VlIHRoZSBkZWZpbml0aW9uIG9mIG11bHRpcGFy
dCBtZXNzYWdlcyBpbg==
ICAgUkZDIDIwNDYuKQ==
ICAgICAhIiMkQFtcXV5ge3x9fiU=
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAg
ICAgICAgW1BhZ2UgMjRd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAg
ICBOb3ZlbWJlciAxOTk2
ICAgICAgICAgICAgICAgICAgICBUYWJsZSAxOiBUaGUgQmFzZTY0IEFscGhhYmV0
ICAgICBWYWx1ZSBFbmNvZGluZyAgVmFsdWUgRW5jb2RpbmcgIFZhbHVlIEVuY29kaW5nICBWYWx1
ZSBFbmNvZGluZw==
ICAgICAgICAgMCBBICAgICAgICAgICAgMTcgUiAgICAgICAgICAgIDM0IGkgICAgICAgICAgICA1
MSB6
ICAgICAgICAgMSBCICAgICAgICAgICAgMTggUyAgICAgICAgICAgIDM1IGogICAgICAgICAgICA1
MiAw
ICAgICAgICAgMiBDICAgICAgICAgICAgMTkgVCAgICAgICAgICAgIDM2IGsgICAgICAgICAgICA1
MyAx
ICAgICAgICAgMyBEICAgICAgICAgICAgMjAgVSAgICAgICAgICAgIDM3IGwgICAgICAgICAgICA1
NCAy
ICAgICAgICAgNCBFICAgICAgICAgICAgMjEgViAgICAgICAgICAgIDM4IG0gICAgICAgICAgICA1
NSAz
ICAgICAgICAgNSBGICAgICAgICAgICAgMjIgVyAgICAgICAgICAgIDM5IG4gICAgICAgICAgICA1
NiA0
ICAgICAgICAgNiBHICAgICAgICAgICAgMjMgWCAgICAgICAgICAgIDQwIG8gICAgICAgICAgICA1
NyA1
ICAgICAgICAgNyBIICAgICAgICAgICAgMjQgWSAgICAgICAgICAgIDQxIHAgICAgICAgICAgICA1
OCA2
ICAgICAgICAgOCBJICAgICAgICAgICAgMjUgWiAgICAgICAgICAgIDQyIHEgICAgICAgICAgICA1
OSA3
ICAgICAgICAgOSBKICAgICAgICAgICAgMjYgYSAgICAgICAgICAgIDQzIHIgICAgICAgICAgICA2
MCA4
ICAgICAgICAxMCBLICAgICAgICAgICAgMjcgYiAgICAgICAgICAgIDQ0IHMgICAgICAgICAgICA2
MSA5
ICAgICAgICAxMSBMICAgICAgICAgICAgMjggYyAgICAgICAgICAgIDQ1IHQgICAgICAgICAgICA2
MiAr
ICAgICAgICAxMiBNICAgICAgICAgICAgMjkgZCAgICAgICAgICAgIDQ2IHUgICAgICAgICAgICA2
MyAv
ICAgICAgICAxMyBOICAgICAgICAgICAgMzAgZSAgICAgICAgICAgIDQ3IHY=
ICAgICAgICAxNCBPICAgICAgICAgICAgMzEgZiAgICAgICAgICAgIDQ4IHcgICAgICAgICAocGFk
KSA9
ICAgICAgICAxNSBQICAgICAgICAgICAgMzIgZyAgICAgICAgICAgIDQ5IHg=
ICAgICAgICAxNiBRICAgICAgICAgICAgMzMgaCAgICAgICAgICAgIDUwIHk=
This test data is part of rfc2045 which includes all characters a~z A~Z, 0~9 and all symbols,
It is used to test java.util.Base64.Encoder, and will be encoded by org.apache.commons.codec.binary.Base64.java
to test java.util.Base64.Decoder;
Freed & Borenstein Standards Track [Page 1]
RFC 2045 Internet Message Bodies November 1996
These documents are revisions of RFCs 1521, 1522, and 1590, which
themselves were revisions of RFCs 1341 and 1342. An appendix in RFC
2049 describes differences and changes from previous versions.
Table of Contents
1. Introduction ......................................... 3
2. Definitions, Conventions, and Generic BNF Grammar .... 5
3. MIME Header Fields ................................... 8
4. MIME-Version Header Field ............................ 8
5. Content-Type Header Field ............................ 10
6. Content-Transfer-Encoding Header Field ............... 14
7. Content-ID Header Field .............................. 26
8. Content-Description Header Field ..................... 27
9. Additional MIME Header Fields ........................ 27
10. Summary ............................................. 27
11. Security Considerations ............................. 27
12. Authors' Addresses .................................. 28
A. Collected Grammar .................................... 29
Freed & Borenstein Standards Track [Page 7]
RFC 2045 Internet Message Bodies November 1996
3. MIME Header Fields
MIME defines a number of new RFC 822 header fields that are used to
describe the content of a MIME entity. These header fields occur in
at least two contexts:
(1) As part of a regular RFC 822 message header.
(2) In a MIME body part header within a multipart
construct.
The formal definition of these header fields is as follows:
MIME-message-headers := entity-headers
fields
version CRLF
; The ordering of the header
; fields implied by this BNF
; definition should be ignored.
MIME-part-headers := entity-headers
[ fields ]
; Any field not beginning with
; "content-" can have no defined
; meaning and may be ignored.
; The ordering of the header
; fields implied by this BNF
; definition should be ignored.
The syntax of the various specific MIME header fields will be
described in the following sections.
Freed & Borenstein Standards Track [Page 11]
RFC 2045 Internet Message Bodies November 1996
5.1. Syntax of the Content-Type Header Field
In the Augmented BNF notation of RFC 822, a Content-Type header field
value is defined as follows:
content := "Content-Type" ":" type "/" subtype
*(";" parameter)
; Matching of media type and subtype
; is ALWAYS case-insensitive.
type := discrete-type / composite-type
discrete-type := "text" / "image" / "audio" / "video" /
"application" / extension-token
composite-type := "message" / "multipart" / extension-token
extension-token := ietf-token / x-token
ietf-token := <An extension token defined by a
standards-track RFC and registered
with IANA.>
x-token := <The two characters "X-" or "x-" followed, with
no intervening white space, by any token>
subtype := extension-token / iana-token
iana-token := <A publicly-defined extension token. Tokens
of this form must be registered with IANA
as specified in RFC 2048.>
parameter := attribute "=" value
attribute := token
; Matching of attributes
; is ALWAYS case-insensitive.
value := token / quoted-string
token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
or tspecials>
tspecials := "(" / ")" / "<" / ">" / "@" /
"," / ";" / ":" / "\" / <">
"/" / "[" / "]" / "?" / "="
; Must be in quoted-string,
; to use within parameter values
description := "Content-Description" ":" *text
encoding := "Content-Transfer-Encoding" ":" mechanism
entity-headers := [ content CRLF ]
[ encoding CRLF ]
[ id CRLF ]
[ description CRLF ]
*( MIME-extension-field CRLF )
hex-octet := "=" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F")
; Octet must be used for characters > 127, =,
; SPACEs or TABs at the ends of lines, and is
; recommended for any character not listed in
; RFC 2049 as "mail-safe".
RFC 2045 Internet Message Bodies November 1996
must be used. An equal sign as the last character on a
encoded line indicates such a non-significant ("soft")
line break in the encoded text.
Thus if the "raw" form of the line is a single unencoded line that
says:
Now's the time for all folk to come to the aid of their country.
This can be represented, in the Quoted-Printable encoding, as:
Now's the time =
for all folk to come=
to the aid of their country.
Since the hyphen character ("-") may be represented as itself in the
Quoted-Printable encoding, care must be taken, when encapsulating a
quoted-printable encoded body inside one or more multipart entities,
to ensure that the boundary delimiter does not appear anywhere in the
encoded body. (A good strategy is to choose a boundary that includes
a character sequence such as "=_" which can never appear in a
quoted-printable body. See the definition of multipart messages in
RFC 2046.)
!"#$@[\]^`{|}~%
Freed & Borenstein Standards Track [Page 24]
RFC 2045 Internet Message Bodies November 1996
Table 1: The Base64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
VGhpcyB0ZXN0IGRhdGEgaXMgcGFydCBvZiByZmMyMDQ1IHdoaWNoIGluY2x1ZGVzIGFsbCBjaGFyYWN0ZXJzIGF-eiBBflosIDB-OSBhbmQgYWxsIHN5bWJvbHMs
SXQgaXMgdXNlZCB0byB0ZXN0IGphdmEudXRpbC5CYXNlNjQuRW5jb2RlciwgYW5kIHdpbGwgYmUgZW5jb2RlZCBieSBvcmcuYXBhY2hlLmNvbW1vbnMuY29kZWMuYmluYXJ5LkJhc2U2NC5qYXZh
dG8gdGVzdCBqYXZhLnV0aWwuQmFzZTY0LkRlY29kZXI7
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgIFtQYWdlIDFd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
ICAgVGhlc2UgZG9jdW1lbnRzIGFyZSByZXZpc2lvbnMgb2YgUkZDcyAxNTIxLCAxNTIyLCBhbmQgMTU5MCwgd2hpY2g=
ICAgdGhlbXNlbHZlcyB3ZXJlIHJldmlzaW9ucyBvZiBSRkNzIDEzNDEgYW5kIDEzNDIuICBBbiBhcHBlbmRpeCBpbiBSRkM=
ICAgMjA0OSBkZXNjcmliZXMgZGlmZmVyZW5jZXMgYW5kIGNoYW5nZXMgZnJvbSBwcmV2aW91cyB2ZXJzaW9ucy4=
VGFibGUgb2YgQ29udGVudHM=
ICAgMS4gSW50cm9kdWN0aW9uIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgIDM=
ICAgMi4gRGVmaW5pdGlvbnMsIENvbnZlbnRpb25zLCBhbmQgR2VuZXJpYyBCTkYgR3JhbW1hciAuLi4uICAgIDU=
ICAgMy4gTUlNRSBIZWFkZXIgRmllbGRzIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgIDg=
ICAgNC4gTUlNRS1WZXJzaW9uIEhlYWRlciBGaWVsZCAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgIDg=
ICAgNS4gQ29udGVudC1UeXBlIEhlYWRlciBGaWVsZCAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMTA=
ICAgNi4gQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZyBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uICAgMTQ=
ICAgNy4gQ29udGVudC1JRCBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjY=
ICAgOC4gQ29udGVudC1EZXNjcmlwdGlvbiBIZWFkZXIgRmllbGQgLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgOS4gQWRkaXRpb25hbCBNSU1FIEhlYWRlciBGaWVsZHMgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgMTAuIFN1bW1hcnkgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgMTEuIFNlY3VyaXR5IENvbnNpZGVyYXRpb25zIC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjc=
ICAgMTIuIEF1dGhvcnMnIEFkZHJlc3NlcyAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjg=
ICAgQS4gQ29sbGVjdGVkIEdyYW1tYXIgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgMjk=
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgIFtQYWdlIDdd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
My4gIE1JTUUgSGVhZGVyIEZpZWxkcw==
ICAgTUlNRSBkZWZpbmVzIGEgbnVtYmVyIG9mIG5ldyBSRkMgODIyIGhlYWRlciBmaWVsZHMgdGhhdCBhcmUgdXNlZCB0bw==
ICAgZGVzY3JpYmUgdGhlIGNvbnRlbnQgb2YgYSBNSU1FIGVudGl0eS4gIFRoZXNlIGhlYWRlciBmaWVsZHMgb2NjdXIgaW4=
ICAgYXQgbGVhc3QgdHdvIGNvbnRleHRzOg==
ICAgICgxKSAgIEFzIHBhcnQgb2YgYSByZWd1bGFyIFJGQyA4MjIgbWVzc2FnZSBoZWFkZXIu
ICAgICgyKSAgIEluIGEgTUlNRSBib2R5IHBhcnQgaGVhZGVyIHdpdGhpbiBhIG11bHRpcGFydA==
ICAgICAgICAgIGNvbnN0cnVjdC4=
ICAgVGhlIGZvcm1hbCBkZWZpbml0aW9uIG9mIHRoZXNlIGhlYWRlciBmaWVsZHMgaXMgYXMgZm9sbG93czo=
ICAgICBNSU1FLW1lc3NhZ2UtaGVhZGVycyA6PSBlbnRpdHktaGVhZGVycw==
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZHM=
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uIENSTEY=
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IFRoZSBvcmRlcmluZyBvZiB0aGUgaGVhZGVy
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IGZpZWxkcyBpbXBsaWVkIGJ5IHRoaXMgQk5G
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7IGRlZmluaXRpb24gc2hvdWxkIGJlIGlnbm9yZWQu
ICAgICBNSU1FLXBhcnQtaGVhZGVycyA6PSBlbnRpdHktaGVhZGVycw==
ICAgICAgICAgICAgICAgICAgICAgICAgICBbIGZpZWxkcyBd
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IEFueSBmaWVsZCBub3QgYmVnaW5uaW5nIHdpdGg=
ICAgICAgICAgICAgICAgICAgICAgICAgICA7ICJjb250ZW50LSIgY2FuIGhhdmUgbm8gZGVmaW5lZA==
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IG1lYW5pbmcgYW5kIG1heSBiZSBpZ25vcmVkLg==
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IFRoZSBvcmRlcmluZyBvZiB0aGUgaGVhZGVy
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IGZpZWxkcyBpbXBsaWVkIGJ5IHRoaXMgQk5G
ICAgICAgICAgICAgICAgICAgICAgICAgICA7IGRlZmluaXRpb24gc2hvdWxkIGJlIGlnbm9yZWQu
ICAgVGhlIHN5bnRheCBvZiB0aGUgdmFyaW91cyBzcGVjaWZpYyBNSU1FIGhlYWRlciBmaWVsZHMgd2lsbCBiZQ==
ICAgZGVzY3JpYmVkIGluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMu
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgW1BhZ2UgMTFd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
NS4xLiAgU3ludGF4IG9mIHRoZSBDb250ZW50LVR5cGUgSGVhZGVyIEZpZWxk
ICAgSW4gdGhlIEF1Z21lbnRlZCBCTkYgbm90YXRpb24gb2YgUkZDIDgyMiwgYSBDb250ZW50LVR5cGUgaGVhZGVyIGZpZWxk
ICAgdmFsdWUgaXMgZGVmaW5lZCBhcyBmb2xsb3dzOg==
ICAgICBjb250ZW50IDo9ICJDb250ZW50LVR5cGUiICI6IiB0eXBlICIvIiBzdWJ0eXBl
ICAgICAgICAgICAgICAgICooIjsiIHBhcmFtZXRlcik=
ICAgICAgICAgICAgICAgIDsgTWF0Y2hpbmcgb2YgbWVkaWEgdHlwZSBhbmQgc3VidHlwZQ==
ICAgICAgICAgICAgICAgIDsgaXMgQUxXQVlTIGNhc2UtaW5zZW5zaXRpdmUu
ICAgICB0eXBlIDo9IGRpc2NyZXRlLXR5cGUgLyBjb21wb3NpdGUtdHlwZQ==
ICAgICBkaXNjcmV0ZS10eXBlIDo9ICJ0ZXh0IiAvICJpbWFnZSIgLyAiYXVkaW8iIC8gInZpZGVvIiAv
ICAgICAgICAgICAgICAgICAgICAgICJhcHBsaWNhdGlvbiIgLyBleHRlbnNpb24tdG9rZW4=
ICAgICBjb21wb3NpdGUtdHlwZSA6PSAibWVzc2FnZSIgLyAibXVsdGlwYXJ0IiAvIGV4dGVuc2lvbi10b2tlbg==
ICAgICBleHRlbnNpb24tdG9rZW4gOj0gaWV0Zi10b2tlbiAvIHgtdG9rZW4=
ICAgICBpZXRmLXRva2VuIDo9IDxBbiBleHRlbnNpb24gdG9rZW4gZGVmaW5lZCBieSBh
ICAgICAgICAgICAgICAgICAgICBzdGFuZGFyZHMtdHJhY2sgUkZDIGFuZCByZWdpc3RlcmVk
ICAgICAgICAgICAgICAgICAgICB3aXRoIElBTkEuPg==
ICAgICB4LXRva2VuIDo9IDxUaGUgdHdvIGNoYXJhY3RlcnMgIlgtIiBvciAieC0iIGZvbGxvd2VkLCB3aXRo
ICAgICAgICAgICAgICAgICBubyBpbnRlcnZlbmluZyB3aGl0ZSBzcGFjZSwgYnkgYW55IHRva2VuPg==
ICAgICBzdWJ0eXBlIDo9IGV4dGVuc2lvbi10b2tlbiAvIGlhbmEtdG9rZW4=
ICAgICBpYW5hLXRva2VuIDo9IDxBIHB1YmxpY2x5LWRlZmluZWQgZXh0ZW5zaW9uIHRva2VuLiBUb2tlbnM=
ICAgICAgICAgICAgICAgICAgICBvZiB0aGlzIGZvcm0gbXVzdCBiZSByZWdpc3RlcmVkIHdpdGggSUFOQQ==
ICAgICAgICAgICAgICAgICAgICBhcyBzcGVjaWZpZWQgaW4gUkZDIDIwNDguPg==
ICAgICBwYXJhbWV0ZXIgOj0gYXR0cmlidXRlICI9IiB2YWx1ZQ==
ICAgICBhdHRyaWJ1dGUgOj0gdG9rZW4=
ICAgICAgICAgICAgICAgICAgOyBNYXRjaGluZyBvZiBhdHRyaWJ1dGVz
ICAgICAgICAgICAgICAgICAgOyBpcyBBTFdBWVMgY2FzZS1pbnNlbnNpdGl2ZS4=
ICAgICB2YWx1ZSA6PSB0b2tlbiAvIHF1b3RlZC1zdHJpbmc=
ICAgICB0b2tlbiA6PSAxKjxhbnkgKFVTLUFTQ0lJKSBDSEFSIGV4Y2VwdCBTUEFDRSwgQ1RMcyw=
ICAgICAgICAgICAgICAgICBvciB0c3BlY2lhbHM-
ICAgICB0c3BlY2lhbHMgOj0gICIoIiAvICIpIiAvICI8IiAvICI-IiAvICJAIiAv
ICAgICAgICAgICAgICAgICAgICIsIiAvICI7IiAvICI6IiAvICJcIiAvIDwiPg==
ICAgICAgICAgICAgICAgICAgICIvIiAvICJbIiAvICJdIiAvICI_IiAvICI9Ig==
ICAgICAgICAgICAgICAgICAgIDsgTXVzdCBiZSBpbiBxdW90ZWQtc3RyaW5nLA==
ICAgICAgICAgICAgICAgICAgIDsgdG8gdXNlIHdpdGhpbiBwYXJhbWV0ZXIgdmFsdWVz
ICAgICBkZXNjcmlwdGlvbiA6PSAiQ29udGVudC1EZXNjcmlwdGlvbiIgIjoiICp0ZXh0
ICAgICBlbmNvZGluZyA6PSAiQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZyIgIjoiIG1lY2hhbmlzbQ==
ICAgICBlbnRpdHktaGVhZGVycyA6PSBbIGNvbnRlbnQgQ1JMRiBd
ICAgICAgICAgICAgICAgICAgICBbIGVuY29kaW5nIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICBbIGlkIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICBbIGRlc2NyaXB0aW9uIENSTEYgXQ==
ICAgICAgICAgICAgICAgICAgICAqKCBNSU1FLWV4dGVuc2lvbi1maWVsZCBDUkxGICk=
ICAgICBoZXgtb2N0ZXQgOj0gIj0iIDIoRElHSVQgLyAiQSIgLyAiQiIgLyAiQyIgLyAiRCIgLyAiRSIgLyAiRiIp
ICAgICAgICAgICAgICAgOyBPY3RldCBtdXN0IGJlIHVzZWQgZm9yIGNoYXJhY3RlcnMgPiAxMjcsID0s
ICAgICAgICAgICAgICAgOyBTUEFDRXMgb3IgVEFCcyBhdCB0aGUgZW5kcyBvZiBsaW5lcywgYW5kIGlz
ICAgICAgICAgICAgICAgOyByZWNvbW1lbmRlZCBmb3IgYW55IGNoYXJhY3RlciBub3QgbGlzdGVkIGlu
ICAgICAgICAgICAgICAgOyBSRkMgMjA0OSBhcyAibWFpbC1zYWZlIi4=
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
ICAgICAgICAgIG11c3QgYmUgdXNlZC4gIEFuIGVxdWFsIHNpZ24gYXMgdGhlIGxhc3QgY2hhcmFjdGVyIG9uIGE=
ICAgICAgICAgIGVuY29kZWQgbGluZSBpbmRpY2F0ZXMgc3VjaCBhIG5vbi1zaWduaWZpY2FudCAoInNvZnQiKQ==
ICAgICAgICAgIGxpbmUgYnJlYWsgaW4gdGhlIGVuY29kZWQgdGV4dC4=
ICAgVGh1cyBpZiB0aGUgInJhdyIgZm9ybSBvZiB0aGUgbGluZSBpcyBhIHNpbmdsZSB1bmVuY29kZWQgbGluZSB0aGF0
ICAgc2F5czo=
ICAgICBOb3cncyB0aGUgdGltZSBmb3IgYWxsIGZvbGsgdG8gY29tZSB0byB0aGUgYWlkIG9mIHRoZWlyIGNvdW50cnku
ICAgVGhpcyBjYW4gYmUgcmVwcmVzZW50ZWQsIGluIHRoZSBRdW90ZWQtUHJpbnRhYmxlIGVuY29kaW5nLCBhczo=
ICAgICBOb3cncyB0aGUgdGltZSA9
ICAgICBmb3IgYWxsIGZvbGsgdG8gY29tZT0=
ICAgICAgdG8gdGhlIGFpZCBvZiB0aGVpciBjb3VudHJ5Lg==
ICAgU2luY2UgdGhlIGh5cGhlbiBjaGFyYWN0ZXIgKCItIikgbWF5IGJlIHJlcHJlc2VudGVkIGFzIGl0c2VsZiBpbiB0aGU=
ICAgUXVvdGVkLVByaW50YWJsZSBlbmNvZGluZywgY2FyZSBtdXN0IGJlIHRha2VuLCB3aGVuIGVuY2Fwc3VsYXRpbmcgYQ==
ICAgcXVvdGVkLXByaW50YWJsZSBlbmNvZGVkIGJvZHkgaW5zaWRlIG9uZSBvciBtb3JlIG11bHRpcGFydCBlbnRpdGllcyw=
ICAgdG8gZW5zdXJlIHRoYXQgdGhlIGJvdW5kYXJ5IGRlbGltaXRlciBkb2VzIG5vdCBhcHBlYXIgYW55d2hlcmUgaW4gdGhl
ICAgZW5jb2RlZCBib2R5LiAgKEEgZ29vZCBzdHJhdGVneSBpcyB0byBjaG9vc2UgYSBib3VuZGFyeSB0aGF0IGluY2x1ZGVz
ICAgYSBjaGFyYWN0ZXIgc2VxdWVuY2Ugc3VjaCBhcyAiPV8iIHdoaWNoIGNhbiBuZXZlciBhcHBlYXIgaW4gYQ==
ICAgcXVvdGVkLXByaW50YWJsZSBib2R5LiAgU2VlIHRoZSBkZWZpbml0aW9uIG9mIG11bHRpcGFydCBtZXNzYWdlcyBpbg==
ICAgUkZDIDIwNDYuKQ==
ICAgICAhIiMkQFtcXV5ge3x9fiU=
RnJlZWQgJiBCb3JlbnN0ZWluICAgICAgICAgIFN0YW5kYXJkcyBUcmFjayAgICAgICAgICAgICAgICAgICAgW1BhZ2UgMjRd
UkZDIDIwNDUgICAgICAgICAgICAgICAgSW50ZXJuZXQgTWVzc2FnZSBCb2RpZXMgICAgICAgICAgICBOb3ZlbWJlciAxOTk2
ICAgICAgICAgICAgICAgICAgICBUYWJsZSAxOiBUaGUgQmFzZTY0IEFscGhhYmV0
ICAgICBWYWx1ZSBFbmNvZGluZyAgVmFsdWUgRW5jb2RpbmcgIFZhbHVlIEVuY29kaW5nICBWYWx1ZSBFbmNvZGluZw==
ICAgICAgICAgMCBBICAgICAgICAgICAgMTcgUiAgICAgICAgICAgIDM0IGkgICAgICAgICAgICA1MSB6
ICAgICAgICAgMSBCICAgICAgICAgICAgMTggUyAgICAgICAgICAgIDM1IGogICAgICAgICAgICA1MiAw
ICAgICAgICAgMiBDICAgICAgICAgICAgMTkgVCAgICAgICAgICAgIDM2IGsgICAgICAgICAgICA1MyAx
ICAgICAgICAgMyBEICAgICAgICAgICAgMjAgVSAgICAgICAgICAgIDM3IGwgICAgICAgICAgICA1NCAy
ICAgICAgICAgNCBFICAgICAgICAgICAgMjEgViAgICAgICAgICAgIDM4IG0gICAgICAgICAgICA1NSAz
ICAgICAgICAgNSBGICAgICAgICAgICAgMjIgVyAgICAgICAgICAgIDM5IG4gICAgICAgICAgICA1NiA0
ICAgICAgICAgNiBHICAgICAgICAgICAgMjMgWCAgICAgICAgICAgIDQwIG8gICAgICAgICAgICA1NyA1
ICAgICAgICAgNyBIICAgICAgICAgICAgMjQgWSAgICAgICAgICAgIDQxIHAgICAgICAgICAgICA1OCA2
ICAgICAgICAgOCBJICAgICAgICAgICAgMjUgWiAgICAgICAgICAgIDQyIHEgICAgICAgICAgICA1OSA3
ICAgICAgICAgOSBKICAgICAgICAgICAgMjYgYSAgICAgICAgICAgIDQzIHIgICAgICAgICAgICA2MCA4
ICAgICAgICAxMCBLICAgICAgICAgICAgMjcgYiAgICAgICAgICAgIDQ0IHMgICAgICAgICAgICA2MSA5
ICAgICAgICAxMSBMICAgICAgICAgICAgMjggYyAgICAgICAgICAgIDQ1IHQgICAgICAgICAgICA2MiAr
ICAgICAgICAxMiBNICAgICAgICAgICAgMjkgZCAgICAgICAgICAgIDQ2IHUgICAgICAgICAgICA2MyAv
ICAgICAgICAxMyBOICAgICAgICAgICAgMzAgZSAgICAgICAgICAgIDQ3IHY=
ICAgICAgICAxNCBPICAgICAgICAgICAgMzEgZiAgICAgICAgICAgIDQ4IHcgICAgICAgICAocGFkKSA9
ICAgICAgICAxNSBQICAgICAgICAgICAgMzIgZyAgICAgICAgICAgIDQ5IHg=
ICAgICAgICAxNiBRICAgICAgICAgICAgMzMgaCAgICAgICAgICAgIDUwIHk=
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册