All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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."); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy