com.siashan.toolkit.crypt.binary.Base64 Maven / Gradle / Ivy
package com.siashan.toolkit.crypt.binary;
import com.siashan.toolkit.crypt.CodecPolicy;
import com.siashan.toolkit.crypt.util.StringUtils;
import java.math.BigInteger;
import java.util.Objects;
/**
* Base64 算法工具类
*
* @author siashan
* @since 1.0.7
*/
public class Base64 extends BaseNCodec {
/**
* BASE32字符的长度为6位
* 它们是由3个八位字节组成的块形成24位字符串,
* 转换为4个BASE64字符.
*/
private static final int BITS_PER_ENCODED_BYTE = 6;
private static final int BYTES_PER_UNENCODED_BLOCK = 3;
private static final int BYTES_PER_ENCODED_BLOCK = 4;
/**
* 此数组是一个查找表,它将6位正整数索引值转换为RFC 2045表1中指定的“Base64字母表”等效值。
*
*/
private static final byte[] STANDARD_ENCODE_TABLE = {
'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', '+', '/'
};
/**
* 这是上面标准_ENCODE_表的副本,但带有+和/
*
* 更改为-和u以使编码的Base64结果更加URL安全。
*
* 此表仅在Base64的模式设置为URL-SAFE时使用。
*/
private static final byte[] URL_SAFE_ENCODE_TABLE = {
'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', '-', '_'
};
/**
* 此数组是一个查找表,用于转换从“Base64字母表”(如指定)中提取的Unicode字符
*
* 在RFC 2045的表1中)转换为其6位正整数等价物。不在Base64中的字符
*
* 但在数组范围内的字母表转换为-1。
*
* 注:“+”和“-”都解码为62.“/”和“_”都解码为63
*
* URL_安全和标准base64(另一方面,编码器需要提前知道要发出什么)。
*
*/
private static final byte[] DECODE_TABLE = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - /
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z
};
/**
* Base64使用6位字段。
*/
/**
* 用于提取6位的掩码,编码时使用
*/
private static final int MASK_6BITS = 0x3f;
/**
* 用于提取4位的掩码,用于解码最终尾随字符.
*/
private static final int MASK_4BITS = 0xf;
/**
* 用于提取2位的掩码,用于解码最终尾随字符.
*/
private static final int MASK_2BITS = 0x3;
/**
* Base64 解码
*
* @param base64Data 包含Base64数据的字节数组
* @return 包含解码数据的数组.
* @since v1.0.7
*/
public static byte[] decodeBase64(final byte[] base64Data) {
return new Base64().decode(base64Data);
}
/**
* Base64 解码
*
* @param base64String 包含Base64数据的字符串
* @return 包含解码数据的数组
* @since 1.0.7
*/
public static byte[] decodeBase64(final String base64String) {
return new Base64().decode(base64String);
}
// 用于加密的整数编码的实现
/**
* 根据加密标准(如W3C的XML签名)解码字节64编码的整数.
*
* @param pArray 包含base64字符数据的字节数组
* @return BigInteger
* @since 1.0.7
*/
public static BigInteger decodeInteger(final byte[] pArray) {
return new BigInteger(1, decodeBase64(pArray));
}
/**
* 使用base64算法对二进制数据进行编码,但不分块输出.
*
* @param binaryData 要加密的二进制数据
* @return 字节[],在UTF-8表示形式中包含Base64字符。
*/
public static byte[] encodeBase64(final byte[] binaryData) {
return encodeBase64(binaryData, false);
}
/**
* 使用base64算法对二进制数据进行编码,可选地将输出分块为76个字符块.
*
* @param binaryData
* 包含要编码的二进制数据的数组。
* @param isChunked
* 如果{@code true},此编码器将base64输出分块为76个字符块
* @return Base64编码数据。
* @throws IllegalArgumentException
* 当输入数组需要大于{@link Integer#MAX_VALUE}的输出数组时引发
*/
public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) {
return encodeBase64(binaryData, isChunked, false);
}
/**
* 使用base64算法对二进制数据进行编码,可选地将输出分块为76个字符块.
*
* @param binaryData
* 包含要编码的二进制数据的数组。
* @param isChunked
* 如果{@code true},此编码器将base64输出分块为76个字符块
* @param urlSafe
* 如果{@code true},此编码器将发出-和u,而不是通常的+和/字符。
* 注意:使用URL安全字母表编码时不添加填充.
* @return Base64编码数据.
* @throws IllegalArgumentException
* 当输入数组需要大于{@link Integer#MAX_VALUE}的输出数组时引发
*/
public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {
return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
}
/**
* 使用base64算法对二进制数据进行编码,可选地将输出分块为76个字符块.
*
* @param binaryData
* 包含要编码的二进制数据的数组.
* @param isChunked
* 如果{@code true},此编码器将base64输出分块为76个字符块
* @param urlSafe
* 如果{@code true},此编码器将发出-和u,而不是通常的+和/字符.
* 注意:使用URL安全字母表编码时不添加填充.
* @param maxResultSize
* 要接受的最大结果大小。
* @return Base64编码数据.
* @throws IllegalArgumentException
* 当输入数组需要大于maxResultSize的输出数组时引发
*/
public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked,
final boolean urlSafe, final int maxResultSize) {
if (binaryData == null || binaryData.length == 0) {
return binaryData;
}
// Create this so can use the super-class method
// Also ensures that the same roundings are performed by the ctor and the code
final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
final long len = b64.getEncodedLength(binaryData);
if (len > maxResultSize) {
throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
len +
") than the specified maximum size of " +
maxResultSize);
}
return b64.encode(binaryData);
}
/**
* 使用base64算法对二进制数据进行编码,并将编码后的输出分块为76个字符块
*
* @param binaryData
* 要编码的二进制数据
* @return 在76个字符块中分块的Base64个字符
*/
public static byte[] encodeBase64Chunked(final byte[] binaryData) {
return encodeBase64(binaryData, true);
}
/**
* 使用base64算法对二进制数据进行编码,但不分块输出.
*
*
* @param binaryData
* 要编码的二进制数据
* @return 包含Base64字符的字符串.
*/
public static String encodeBase64String(final byte[] binaryData) {
return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
}
/**
* 使用base64算法的URL安全变体对二进制数据进行编码,但不分块输出。这个
* url安全变体发出-和u,而不是+和/字符。
* 注意:未添加任何填充.
* @param binaryData
* 要编码的二进制数据
* @return 字节[],在UTF-8表示形式中包含Base64字符。
*/
public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
return encodeBase64(binaryData, false, true);
}
/**
* 使用base64算法的URL安全变体对二进制数据进行编码,但不分块输出。这个url安全变体发出-和u,而不是+和/字符。
* 注意:未添加任何填充.
* @param binaryData
* 要编码的二进制数据
* @return 包含Base64字符的字符串
*/
public static String encodeBase64URLSafeString(final byte[] binaryData) {
return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true));
}
/**
* 根据加密标准(如W3C的XML签名)编码为字节64编码的整数.
*
* @param bigInteger
* 大整数
* @return 包含base64字符数据的字节数组
* @throws NullPointerException
* 如果传入null
*/
public static byte[] encodeInteger(final BigInteger bigInteger) {
Objects.requireNonNull(bigInteger, "bigInteger");
return encodeBase64(toIntegerBytes(bigInteger), false);
}
/**
* 返回{@code octet}是否在基64字母中.
*
* @param octet
* 要测试的值
* @return {@code true}如果该值是在基64字母表中定义的,否则{@code false}。
*/
public static boolean isBase64(final byte octet) {
return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1);
}
/**
* 测试给定的字节数组,以查看它是否仅包含Base64字母表中的有效字符。目前
* 方法将空白视为有效。
*
* @param arrayOctet
* 要测试的字节数组
* @return {@code true}如果所有字节都是Base64字母表中的有效字符,或者如果字节数组为空;否则为{@code false}
*/
public static boolean isBase64(final byte[] arrayOctet) {
for (int i = 0; i < arrayOctet.length; i++) {
if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
return false;
}
}
return true;
}
/**
*测试给定字符串,查看它是否仅包含Base64字母表中的有效字符。目前方法将空白视为有效。
*
* @param base64
* 要测试的字符串
* @return {@code true}如果字符串中的所有字符都是Base64字母表中的有效字符,或者字符串为空;{@code false},否则为
*/
public static boolean isBase64(final String base64) {
return isBase64(StringUtils.getBytesUtf8(base64));
}
/**
* 返回不带符号位的{@code biginger}的字节数组表示形式.
*
* @param bigInt
* 要转换的{@code biginger}
* @return BigInteger参数的字节数组表示形式
*/
static byte[] toIntegerBytes(final BigInteger bigInt) {
int bitlen = bigInt.bitLength();
// round bitlen
bitlen = ((bitlen + 7) >> 3) << 3;
final byte[] bigBytes = bigInt.toByteArray();
if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
return bigBytes;
}
// set up params for copying everything but sign bit
int startSrc = 0;
int len = bigBytes.length;
// if bigInt is exactly byte-aligned, just skip signbit in copy
if ((bigInt.bitLength() % 8) == 0) {
startSrc = 1;
len--;
}
final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
final byte[] resizedBytes = new byte[bitlen / 8];
System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
return resizedBytes;
}
/**
* 要使用的编码表:标准或URL安全。注意:上面的DECODE_表保持静态,因为它可以
* 要解码标准和URL_安全流,但encodeTable必须是成员变量,以便切换
* 在这两种模式之间。
*/
private final byte[] encodeTable;
// 当前只有一个解码表;保持与Base32代码的一致性
private final byte[] decodeTable = DECODE_TABLE;
/**
*用于编码的行分隔符。解码时不使用。仅当lineLength>;0
*/
private final byte[] lineSeparator;
/**
* 便利性变量,帮助我们确定缓冲区何时会用完并需要调整大小。
* {@code decodeSize=3+lineSeparator.length;}
*/
private final int decodeSize;
/**
* 便利性变量,帮助我们确定缓冲区何时会用完并需要调整大小。
* {@code encodeSize=4+lineSeparator.length;}
*/
private final int encodeSize;
/**
* 创建用于解码(所有模式)和在URL不安全模式下编码的Base64编解码器。
*
* 编码时,行长度为0(无分块),编码表为标准编码表。
*
*
*
* 解码时,支持所有变体。
*
*/
public Base64() {
this(0);
}
/**
* 创建用于在给定URL安全模式下解码(所有模式)和编码的Base64编解码器.
*
* 当编码行长度为76时,行分隔符为CRLF,编码表为标准编码表.
*
*
*
* 解码时,支持所有变体。
*
*
* @param urlSafe
* 如果{@code true},则使用URL安全编码。在大多数情况下,这应该设置为{@code false}。
*/
public Base64(final boolean urlSafe) {
this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
}
/**
* 创建用于解码(所有模式)和在URL不安全模式下编码的Base64编解码器.
*
* 当在构造函数中给出编码行长度时,行分隔符为CRLF,编码表为标准编码表。
*
*
* 在编码数据中,不是4的倍数的行长度实际上仍然是4的倍数。
*
*
* 解码时,支持所有变体。
*
*
* @param lineLength
* 每行编码数据最多为给定长度(四舍五入到最接近的整数倍)4).
* 如果线宽<;=0,则输出将不会被划分为行(块)。当解码。
*/
public Base64(final int lineLength) {
this(lineLength, CHUNK_SEPARATOR);
}
/**
* 创建用于解码(所有模式)和在URL不安全模式下编码的Base64编解码器。
*
* 编码时,构造函数中给出了行长度和行分隔符,编码表为标准编码表.
*
*
* 在编码数据中,不是4的倍数的行长度实际上仍然是4的倍数.
*
*
* 解码时,支持所有变体.
*
*
* @param lineLength
* 每行编码数据最多为给定长度(四舍五入到最接近的整数倍)4).
* 如果线宽<;=0,则输出将不会被划分为行(块)。当解码。
* @param lineSeparator
* 每行编码数据都将以这个字节序列结束。
* @throws IllegalArgumentException
* 当提供的lineSeparator包含一些base64字符时引发。
*/
public Base64(final int lineLength, final byte[] lineSeparator) {
this(lineLength, lineSeparator, false);
}
/**
* 创建用于解码(所有模式)和在URL不安全模式下编码的Base64编解码器。
*
* 编码时,构造函数中给出了行长度和行分隔符,编码表为标准编码表.
*
*
* 在编码数据中,不是4的倍数的行长度实际上仍然是4的倍数。
*
*
* 解码时,支持所有变体。
*
*
* @param lineLength
* 每行编码数据最多为给定长度(四舍五入到最接近的整数倍)4).
* 如果线宽<;=0,则输出将不会被划分为行(块)。当解码。
* @param lineSeparator
* 每行编码数据都将以这个字节序列结束.
* @param urlSafe
* 我们不发射“+”和“/”,而是分别发射“-”和“10;”。urlSafe仅应用于编码操作。解码可以无缝地处理这两种模式。
* 注意:使用URL安全字母表时不添加填充.
* @throws IllegalArgumentException
* 当{@code lineSeparator}包含Base64个字符时引发.
*/
public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) {
this(lineLength, lineSeparator, urlSafe, DECODING_POLICY_DEFAULT);
}
/**
* 创建用于解码(所有模式)和在URL不安全模式下编码的Base64编解码器.
*
* 编码时,构造函数中给出了行长度和行分隔符,编码表为标准编码表.
*
*
* 在编码数据中,不是4的倍数的行长度实际上仍然是4的倍数。
*
*
* 解码时,支持所有变体.
*
*
* @param lineLength
* 每行编码数据最多为给定长度(四舍五入到最接近的整数倍)4).
* 如果线宽<;=0,则输出将不会被划分为行(块)。当解码。
* @param lineSeparator
* 每行编码数据都将以这个字节序列结束。
* @param urlSafe
* 我们不发射“+”和“/”,而是分别发射“-”和“10;”。urlSafe仅应用于编码操作。解码可以无缝地处理这两种模式。
* 注意:使用URL安全字母表时不添加填充.
* @param decodingPolicy 解码策略.
* @throws IllegalArgumentException
* 当{@code lineSeparator}包含Base64个字符时引发.
*/
public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, final CodecPolicy decodingPolicy) {
super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
lineLength,
lineSeparator == null ? 0 : lineSeparator.length,
PAD_DEFAULT,
decodingPolicy);
// TODO could be simplified if there is no requirement to reject invalid line sep when length <=0
// @see test case Base64Test.testConstructors()
if (lineSeparator != null) {
if (containsAlphabetOrPad(lineSeparator)) {
final String sep = StringUtils.newStringUtf8(lineSeparator);
throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]");
}
if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE
this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length;
this.lineSeparator = new byte[lineSeparator.length];
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
} else {
this.encodeSize = BYTES_PER_ENCODED_BLOCK;
this.lineSeparator = null;
}
} else {
this.encodeSize = BYTES_PER_ENCODED_BLOCK;
this.lineSeparator = null;
}
this.decodeSize = this.encodeSize - 1;
this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
}
// Implementation of the Encoder Interface
/**
*
* 从inPos开始,对所有提供的数据进行无效字节解码。应至少调用两次:一次
* 使用要解码的数据,并在inAvail设置为“-1”时提醒解码器已达到EOF。“-1”
* 解码时不需要调用,但也不会造成伤害。
*
*
* 忽略所有非base64字符。这是处理分块(例如76个字符)数据的方式,因为CR和LF是
* 默认忽略,但对其他字节也有影响。此方法订阅中的垃圾,
* 垃圾处理原理:它不会检查提供的数据的有效性.
*
*
* @param in
* 要进行base64解码的ascii数据的字节[]数组.
* @param inPos
* 开始从中读取数据的位置。
* @param inAvail
* 从输入中可用于解码的字节数。
* @param context
* 要使用的上下文
*/
@Override
void decode(final byte[] in, int inPos, final int inAvail, final Context context) {
if (context.eof) {
return;
}
if (inAvail < 0) {
context.eof = true;
}
for (int i = 0; i < inAvail; i++) {
final byte[] buffer = ensureBufferSize(decodeSize, context);
final byte b = in[inPos++];
if (b == pad) {
// We're done.
context.eof = true;
break;
}
if (b >= 0 && b < DECODE_TABLE.length) {
final int result = DECODE_TABLE[b];
if (result >= 0) {
context.modulus = (context.modulus + 1) % BYTES_PER_ENCODED_BLOCK;
context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;
if (context.modulus == 0) {
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS);
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
}
}
}
}
// Two forms of EOF as far as base64 decoder is concerned: actual
// EOF (-1) and first time '=' character is encountered in stream.
// This approach makes the '=' padding characters completely optional.
if (context.eof && context.modulus != 0) {
final byte[] buffer = ensureBufferSize(decodeSize, context);
// We have some spare bits remaining
// Output all whole multiples of 8 bits and ignore the rest
switch (context.modulus) {
// case 0 : // impossible, as excluded above
case 1: // 6 bits - either ignore entirely, or raise an exception
validateTrailingCharacter();
break;
case 2: // 12 bits = 8 + 4
validateCharacter(MASK_4BITS, context);
context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
break;
case 3: // 18 bits = 8 + 8 + 2
validateCharacter(MASK_2BITS, context);
context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
break;
default:
throw new IllegalStateException("Impossible modulus " + context.modulus);
}
}
}
/**
*
* 从inPos开始,对所有提供的数据进行无效字节编码。必须至少调用两次:一次使用
* 要编码的数据,一旦inAvail设置为“-1”以警告编码器已达到EOF,则最后刷新
* 剩余字节(如果不是3的倍数)。
*
* 注意:使用URL安全字母表编码时不添加填充.
*
* @param in
* 要进行base64编码的二进制数据的字节[]数组。
* @param inPos
* 开始从中读取数据的位置。
* @param inAvail
* 输入中可用于编码的字节数。
* @param context
* 要使用的上下文
*/
@Override
void encode(final byte[] in, int inPos, final int inAvail, final Context context) {
if (context.eof) {
return;
}
// inAvail < 0 is how we're informed of EOF in the underlying data we're
// encoding.
if (inAvail < 0) {
context.eof = true;
if (0 == context.modulus && lineLength == 0) {
return; // no leftovers to process and not using chunking
}
final byte[] buffer = ensureBufferSize(encodeSize, context);
final int savedPos = context.pos;
switch (context.modulus) { // 0-2
case 0: // nothing to do here
break;
case 1: // 8 bits = 6 + 2
// top 6 bits:
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS];
// remaining 2:
buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS];
// URL-SAFE skips the padding to further reduce size.
if (encodeTable == STANDARD_ENCODE_TABLE) {
buffer[context.pos++] = pad;
buffer[context.pos++] = pad;
}
break;
case 2: // 16 bits = 6 + 6 + 4
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS];
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS];
buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS];
// URL-SAFE skips the padding to further reduce size.
if (encodeTable == STANDARD_ENCODE_TABLE) {
buffer[context.pos++] = pad;
}
break;
default:
throw new IllegalStateException("Impossible modulus " + context.modulus);
}
context.currentLinePos += context.pos - savedPos; // keep track of current line position
// if currentPos == 0 we are at the start of a line, so don't add CRLF
if (lineLength > 0 && context.currentLinePos > 0) {
System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
context.pos += lineSeparator.length;
}
} else {
for (int i = 0; i < inAvail; i++) {
final byte[] buffer = ensureBufferSize(encodeSize, context);
context.modulus = (context.modulus + 1) % BYTES_PER_UNENCODED_BLOCK;
int b = in[inPos++];
if (b < 0) {
b += 256;
}
context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE
if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS];
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS];
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS];
buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS];
context.currentLinePos += BYTES_PER_ENCODED_BLOCK;
if (lineLength > 0 && lineLength <= context.currentLinePos) {
System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
context.pos += lineSeparator.length;
context.currentLinePos = 0;
}
}
}
}
}
/**
* 返回{@code octet}是否在Base64字母表中.
*
* @param octet
* 要测试的值
* @return {@code true}如果该值是在Base64字母表中定义的{@code false}否则.
*/
@Override
protected boolean isInAlphabet(final byte octet) {
return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1;
}
/**
* 返回当前的编码模式。如果我们是URL安全的,则为True,否则为false.
*
* @return 如果我们处于URL安全模式,则为true,否则为false
*/
public boolean isUrlSafe() {
return this.encodeTable == URL_SAFE_ENCODE_TABLE;
}
/**
* 验证是否可以在上下文中解码最后的尾随字符一组可能的基64值。
*
* 如果提供的掩码中的低位为零,则该字符有效。这
* 用于测试将被丢弃的位中最后一个尾随的base-64位是否为零.
*
* @param emptyBitsMask 应为空的低位掩码
* @param context 要使用的上下文
*
* @throws IllegalArgumentException 如果正在检查的位包含任何非零值
*/
private void validateCharacter(final int emptyBitsMask, final Context context) {
if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) {
throw new IllegalArgumentException(
"Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
"Expected the discarded bits from the character to be zero.");
}
}
/**
* 验证解码是否允许不可删除的整个尾随字符
* 用于一个完整的字节。
*
* @throws IllegalArgumentException 如果启用了严格解码
*/
private void validateTrailingCharacter() {
if (isStrictDecoding()) {
throw new IllegalArgumentException(
"Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
"Decoding requires at least two trailing 6-bit characters to create bytes.");
}
}
}