From 3e7dd16c4341e598a6e8bdc37590a79f55b04407 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 20 Mar 2022 20:46:32 +0800 Subject: [PATCH] fix BaseN --- CHANGELOG.md | 3 +- .../java/cn/hutool/core/codec/Base32.java | 214 +++++++---------- .../cn/hutool/core/codec/Base32Codec.java | 215 ++++++++++++++++++ .../cn/hutool/core/codec/Base58Codec.java | 195 ++++++++++------ .../cn/hutool/core/codec/Base64Decoder.java | 26 +-- .../java/cn/hutool/core/codec/PunyCode.java | 10 +- .../hutool/core/lang/mutable/MutableInt.java | 6 +- .../java/cn/hutool/core/codec/Base32Test.java | 19 ++ 8 files changed, 451 insertions(+), 237 deletions(-) create mode 100755 hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 469cfb88e..70f423188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ * 【json 】 【可能兼容问题】修改JSONObject结构,继承自MapWrapper * 【core 】 【可能兼容问题】BeanCopier重构,新建XXXCopier,删除XXXValueProvider * 【core 】 【可能兼容问题】URLEncoder废弃,URLEncoderUtil使用RFC3986 -* 【core 】 【可能兼容问题】增加Base32.encode不足位数补= +* 【core 】 【可能兼容问题】Base32分离编码和解码,以便减少数据加载,支持Hex模式 +* 【core 】 【不兼容问题】PunyCode参数由String改为Charsequence ### 🐣新特性 * 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base32.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base32.java index 1fa7db4cb..80c87b7e3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Base32.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base32.java @@ -6,85 +6,28 @@ import cn.hutool.core.util.StrUtil; import java.nio.charset.Charset; /** - * Base32 - encodes and decodes RFC3548 Base32 (see http://www.faqs.org/rfcs/rfc3548.html )
+ * Base32 - encodes and decodes RFC4648 Base32 (see https://datatracker.ietf.org/doc/html/rfc4648#section-6 )
* base32就是用32(2的5次方)个特定ASCII码来表示256个ASCII码。
- * 所以,5个ASCII字符经过base32编码后会变为8个字符(公约数为40),长度增加3/5.不足8n用“=”补足。 - * see http://blog.csdn.net/earbao/article/details/44453937 - * @author Looly + * 所以,5个ASCII字符经过base32编码后会变为8个字符(公约数为40),长度增加3/5.不足8n用“=”补足。
+ * 根据RFC4648 Base32规范,支持两种模式: + * * + * @author Looly */ public class Base32 { - - private Base32() {} - - private static final String BASE32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - private static final int[] BASE32_LOOKUP = {// - 0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // '0', '1', '2', '3', '4', '5', '6', '7' - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // '8', '9', ':', ';', '<', '=', '>', '?' - 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' - 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' - 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' - 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_' - 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g' - 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' - 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' - 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL' - }; - private static final int[] BASE32_FILL = {-1, 4, 1, 6, 3}; - //----------------------------------------------------------------------------------------- encode + /** * 编码 + * * @param bytes 数据 * @return base32 */ public static String encode(final byte[] bytes) { - int i = 0; - int index = 0; - int digit; - int currByte; - int nextByte; - - int encodeLen = bytes.length * 8 / 5; - if (encodeLen != 0) { - encodeLen = encodeLen + 1 + BASE32_FILL[(bytes.length * 8) % 5]; - } - - StringBuilder base32 = new StringBuilder(encodeLen); - - while (i < bytes.length) { - // unsign - currByte = (bytes[i] >= 0) ? bytes[i] : (bytes[i] + 256); - - /* Is the current digit going to span a byte boundary? */ - if (index > 3) { - if ((i + 1) < bytes.length) { - nextByte = (bytes[i + 1] >= 0) ? bytes[i + 1] : (bytes[i + 1] + 256); - } else { - nextByte = 0; - } - - digit = currByte & (0xFF >> index); - index = (index + 5) % 8; - digit <<= index; - digit |= nextByte >> (8 - index); - i++; - } else { - digit = (currByte >> (8 - (index + 5))) & 0x1F; - index = (index + 5) % 8; - if (index == 0) { - i++; - } - } - base32.append(BASE32_CHARS.charAt(digit)); - } - - // 末尾补充不足长度的 - while(base32.length() < encodeLen){ - base32.append('='); - } - - return base32.toString(); + return Base32Codec.INSTANCE.encode(bytes); } /** @@ -100,18 +43,7 @@ public class Base32 { /** * base32编码 * - * @param source 被编码的base32字符串 - * @param charset 字符集 - * @return 被加密后的字符串 - */ - public static String encode(String source, String charset) { - return encode(StrUtil.bytes(source, charset)); - } - - /** - * base32编码 - * - * @param source 被编码的base32字符串 + * @param source 被编码的base32字符串 * @param charset 字符集 * @return 被加密后的字符串 */ @@ -119,55 +51,47 @@ public class Base32 { return encode(StrUtil.bytes(source, charset)); } + /** + * 编码 + * + * @param bytes 数据(Hex模式) + * @return base32 + */ + public static String encodeHex(final byte[] bytes) { + return Base32Codec.INSTANCE.encode(bytes, true); + } + + /** + * base32编码(Hex模式) + * + * @param source 被编码的base32字符串 + * @return 被加密后的字符串 + */ + public static String encodeHex(String source) { + return encodeHex(source, CharsetUtil.CHARSET_UTF_8); + } + + /** + * base32编码(Hex模式) + * + * @param source 被编码的base32字符串 + * @param charset 字符集 + * @return 被加密后的字符串 + */ + public static String encodeHex(String source, Charset charset) { + return encodeHex(StrUtil.bytes(source, charset)); + } + //----------------------------------------------------------------------------------------- decode + /** * 解码 + * * @param base32 base32编码 * @return 数据 */ - public static byte[] decode(final String base32) { - int i, index, lookup, offset, digit; - int len = base32.endsWith("=") ? base32.indexOf("=") * 5 / 8 : base32.length() * 5 / 8; - byte[] bytes = new byte[len]; - - for (i = 0, index = 0, offset = 0; i < base32.length(); i++) { - lookup = base32.charAt(i) - '0'; - - /* Skip chars outside the lookup table */ - if (lookup < 0 || lookup >= BASE32_LOOKUP.length) { - continue; - } - - digit = BASE32_LOOKUP[lookup]; - - /* If this digit is not in the table, ignore it */ - if (digit == 0xFF) { - continue; - } - - if (index <= 3) { - index = (index + 5) % 8; - if (index == 0) { - bytes[offset] |= digit; - offset++; - if (offset >= bytes.length) { - break; - } - } else { - bytes[offset] |= digit << (8 - index); - } - } else { - index = (index + 5) % 8; - bytes[offset] |= (digit >>> index); - offset++; - - if (offset >= bytes.length) { - break; - } - bytes[offset] |= digit << (8 - index); - } - } - return bytes; + public static byte[] decode(String base32) { + return Base32Codec.INSTANCE.decode(base32); } /** @@ -183,22 +107,42 @@ public class Base32 { /** * base32解码 * - * @param source 被解码的base32字符串 - * @param charset 字符集 - * @return 被加密后的字符串 - */ - public static String decodeStr(String source, String charset) { - return StrUtil.str(decode(source), charset); - } - - /** - * base32解码 - * - * @param source 被解码的base32字符串 + * @param source 被解码的base32字符串 * @param charset 字符集 * @return 被加密后的字符串 */ public static String decodeStr(String source, Charset charset) { return StrUtil.str(decode(source), charset); } + + /** + * 解码 + * + * @param base32 base32编码 + * @return 数据 + */ + public static byte[] decodeHex(String base32) { + return Base32Codec.INSTANCE.decode(base32, true); + } + + /** + * base32解码 + * + * @param source 被解码的base32字符串 + * @return 被加密后的字符串 + */ + public static String decodeStrHex(String source) { + return decodeStrHex(source, CharsetUtil.CHARSET_UTF_8); + } + + /** + * base32解码 + * + * @param source 被解码的base32字符串 + * @param charset 字符集 + * @return 被加密后的字符串 + */ + public static String decodeStrHex(String source, Charset charset) { + return StrUtil.str(decodeHex(source), charset); + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java new file mode 100755 index 000000000..42b926e54 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java @@ -0,0 +1,215 @@ +package cn.hutool.core.codec; + +import java.util.Arrays; + +/** + * Base32 - encodes and decodes RFC4648 Base32 (see https://datatracker.ietf.org/doc/html/rfc4648#section-6 )
+ * base32就是用32(2的5次方)个特定ASCII码来表示256个ASCII码。
+ * 所以,5个ASCII字符经过base32编码后会变为8个字符(公约数为40),长度增加3/5.不足8n用“=”补足。
+ * 根据RFC4648 Base32规范,支持两种模式: + * + * + * @author Looly + * @since 5.8.0 + */ +public class Base32Codec implements Encoder, Decoder { + + public static Base32Codec INSTANCE = new Base32Codec(); + + @Override + public String encode(byte[] data) { + return encode(data, false); + } + + /** + * 编码数据 + * + * @param data 数据 + * @param useHex 是否使用Hex Alphabet + * @return 编码后的Base32字符串 + */ + public String encode(byte[] data, boolean useHex) { + final Base32Encoder encoder = useHex ? Base32Encoder.HEX_ENCODER : Base32Encoder.ENCODER; + return encoder.encode(data); + } + + @Override + public byte[] decode(CharSequence encoded) { + return decode(encoded, false); + } + + /** + * 解码数据 + * + * @param encoded base32字符串 + * @param useHex 是否使用Hex Alphabet + * @return 解码后的内容 + */ + public byte[] decode(CharSequence encoded, boolean useHex) { + final Base32Decoder decoder = useHex ? Base32Decoder.HEX_DECODER : Base32Decoder.DECODER; + return decoder.decode(encoded); + } + + /** + * Bas32编码器 + */ + public static class Base32Encoder implements Encoder { + private static final String DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + private static final String HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + private static final Character DEFAULT_PAD = '='; + private static final int[] BASE32_FILL = {-1, 4, 1, 6, 3}; + + public static final Base32Encoder ENCODER = new Base32Encoder(DEFAULT_ALPHABET, DEFAULT_PAD); + public static final Base32Encoder HEX_ENCODER = new Base32Encoder(HEX_ALPHABET, DEFAULT_PAD); + + private final char[] alphabet; + private final Character pad; + + /** + * 构造 + * + * @param alphabet 自定义编码字母表,见 {@link #DEFAULT_ALPHABET}和 {@link #HEX_ALPHABET} + * @param pad 补位字符 + */ + public Base32Encoder(String alphabet, Character pad) { + this.alphabet = alphabet.toCharArray(); + this.pad = pad; + } + + @Override + public String encode(byte[] data) { + int i = 0; + int index = 0; + int digit; + int currByte; + int nextByte; + + int encodeLen = data.length * 8 / 5; + if (encodeLen != 0) { + encodeLen = encodeLen + 1 + BASE32_FILL[(data.length * 8) % 5]; + } + + StringBuilder base32 = new StringBuilder(encodeLen); + + while (i < data.length) { + // unsign + currByte = (data[i] >= 0) ? data[i] : (data[i] + 256); + + /* Is the current digit going to span a byte boundary? */ + if (index > 3) { + if ((i + 1) < data.length) { + nextByte = (data[i + 1] >= 0) ? data[i + 1] : (data[i + 1] + 256); + } else { + nextByte = 0; + } + + digit = currByte & (0xFF >> index); + index = (index + 5) % 8; + digit <<= index; + digit |= nextByte >> (8 - index); + i++; + } else { + digit = (currByte >> (8 - (index + 5))) & 0x1F; + index = (index + 5) % 8; + if (index == 0) { + i++; + } + } + base32.append(alphabet[digit]); + } + + if (null != pad) { + // 末尾补充不足长度的 + while (base32.length() < encodeLen) { + base32.append(pad.charValue()); + } + } + + return base32.toString(); + } + } + + /** + * Base32解码器 + */ + public static class Base32Decoder implements Decoder { + private static final char BASE_CHAR = '0'; + + public static final Base32Decoder DECODER = new Base32Decoder(Base32Encoder.DEFAULT_ALPHABET); + public static final Base32Decoder HEX_DECODER = new Base32Decoder(Base32Encoder.HEX_ALPHABET); + + private final byte[] lookupTable; + + /** + * 构造 + * + * @param alphabet 编码字母表 + */ + public Base32Decoder(String alphabet) { + lookupTable = new byte[128]; + Arrays.fill(lookupTable, (byte) -1); + + final int length = alphabet.length(); + + char c; + for (int i = 0; i < length; i++) { + c = alphabet.charAt(i); + lookupTable[c - BASE_CHAR] = (byte) i; + // 支持小写字母解码 + if(c >= 'A' && c <= 'Z'){ + lookupTable[Character.toLowerCase(c) - BASE_CHAR] = (byte) i; + } + } + } + + @Override + public byte[] decode(CharSequence encoded) { + int i, index, lookup, offset, digit; + final String base32 = encoded.toString(); + int len = base32.endsWith("=") ? base32.indexOf("=") * 5 / 8 : base32.length() * 5 / 8; + byte[] bytes = new byte[len]; + + for (i = 0, index = 0, offset = 0; i < base32.length(); i++) { + lookup = base32.charAt(i) - BASE_CHAR; + + /* Skip chars outside the lookup table */ + if (lookup < 0 || lookup >= lookupTable.length) { + continue; + } + + digit = lookupTable[lookup]; + + /* If this digit is not in the table, ignore it */ + if (digit < 0) { + continue; + } + + if (index <= 3) { + index = (index + 5) % 8; + if (index == 0) { + bytes[offset] |= digit; + offset++; + if (offset >= bytes.length) { + break; + } + } else { + bytes[offset] |= digit << (8 - index); + } + } else { + index = (index + 5) % 8; + bytes[offset] |= (digit >>> index); + offset++; + + if (offset >= bytes.length) { + break; + } + bytes[offset] |= digit << (8 - index); + } + } + return bytes; + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java index 6e6d02d82..da278731e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java @@ -15,10 +15,6 @@ public class Base58Codec implements Encoder, Decoder, Decoder= 0) { - encoded[--outputStart] = ENCODED_ZERO; - } - // Return encoded string (including encoded leading zeros). - return new String(encoded, outputStart, encoded.length - outputStart); + return Base58Encoder.ENCODER.encode(data); } /** @@ -68,52 +35,130 @@ public class Base58Codec implements Encoder, Decoder { + private static final String DEFAULT_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + public static final Base58Encoder ENCODER = new Base58Encoder(DEFAULT_ALPHABET.toCharArray()); + + private final char[] alphabet; + private final char alphabetZero; + + /** + * 构造 + * + * @param alphabet 编码字母表 + */ + public Base58Encoder(char[] alphabet) { + this.alphabet = alphabet; + alphabetZero = alphabet[0]; + } + + @Override + public String encode(byte[] data) { + if (null == data) { + return null; + } + if (data.length == 0) { + return StrUtil.EMPTY; + } + // 计算开头0的个数 + int zeroCount = 0; + while (zeroCount < data.length && data[zeroCount] == 0) { + ++zeroCount; + } + // 将256位编码转换为58位编码 + data = Arrays.copyOf(data, data.length); // since we modify it in-place + final char[] encoded = new char[data.length * 2]; // upper bound + int outputStart = encoded.length; + for (int inputStart = zeroCount; inputStart < data.length; ) { + encoded[--outputStart] = alphabet[divmod(data, inputStart, 256, 58)]; + if (data[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input. + while (outputStart < encoded.length && encoded[outputStart] == alphabetZero) { + ++outputStart; + } + while (--zeroCount >= 0) { + encoded[--outputStart] = alphabetZero; + } + // Return encoded string (including encoded leading zeros). + return new String(encoded, outputStart, encoded.length - outputStart); + } + } + + /** + * Base58解码器 + * + * @since 5.8.0 + */ + public static class Base58Decoder implements Decoder { + + public static Base58Decoder DECODER = new Base58Decoder(Base58Encoder.DEFAULT_ALPHABET); + + private final byte[] lookupTable; + + /** + * 构造 + * + * @param alphabet 编码字符表 + */ + public Base58Decoder(String alphabet) { + final byte[] lookupTable = new byte['z' + 1]; + Arrays.fill(lookupTable, (byte) -1); + + final int length = alphabet.length(); + for (int i = 0; i < length; i++) { + lookupTable[alphabet.charAt(i)] = (byte) i; + } + this.lookupTable = lookupTable; + } + + @Override + public byte[] decode(CharSequence encoded) { + if (encoded.length() == 0) { + return new byte[0]; + } + // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). + final byte[] input58 = new byte[encoded.length()]; + for (int i = 0; i < encoded.length(); ++i) { + char c = encoded.charAt(i); + int digit = c < 128 ? lookupTable[c] : -1; + if (digit < 0) { + throw new IllegalArgumentException(StrUtil.format("Invalid char '{}' at [{}]", c, i)); + } + input58[i] = (byte) digit; + } + // Count leading zeros. + int zeros = 0; + while (zeros < input58.length && input58[zeros] == 0) { + ++zeros; + } + // Convert base-58 digits to base-256 digits. + byte[] decoded = new byte[encoded.length()]; + int outputStart = decoded.length; + for (int inputStart = zeros; inputStart < input58.length; ) { + decoded[--outputStart] = divmod(input58, inputStart, 58, 256); + if (input58[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Ignore extra leading zeroes that were added during the calculation. + while (outputStart < decoded.length && decoded[outputStart] == 0) { + ++outputStart; + } + // Return decoded data (including original number of leading zeros). + return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); + } } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base64Decoder.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base64Decoder.java index 20d0baad9..92b95cb33 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Base64Decoder.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base64Decoder.java @@ -1,5 +1,6 @@ package cn.hutool.core.codec; +import cn.hutool.core.lang.mutable.MutableInt; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; @@ -87,7 +88,7 @@ public class Base64Decoder { return in; } - final IntWrapper offset = new IntWrapper(pos); + final MutableInt offset = new MutableInt(pos); byte sestet0; byte sestet1; @@ -96,7 +97,7 @@ public class Base64Decoder { int maxPos = pos + length - 1; int octetId = 0; byte[] octet = new byte[length * 3 / 4];// over-estimated if non-base64 characters present - while (offset.value <= maxPos) { + while (offset.intValue() <= maxPos) { sestet0 = getNextValidDecodeByte(in, offset, maxPos); sestet1 = getNextValidDecodeByte(in, offset, maxPos); sestet2 = getNextValidDecodeByte(in, offset, maxPos); @@ -141,11 +142,12 @@ public class Base64Decoder { * @param maxPos 最大位置 * @return 有效字符,如果达到末尾返回 */ - private static byte getNextValidDecodeByte(byte[] in, IntWrapper pos, int maxPos) { + private static byte getNextValidDecodeByte(byte[] in, MutableInt pos, int maxPos) { byte base64Byte; byte decodeByte; - while (pos.value <= maxPos) { - base64Byte = in[pos.value++]; + while (pos.intValue() <= maxPos) { + base64Byte = in[pos.intValue()]; + pos.increment(); if (base64Byte > -1) { decodeByte = DECODE_TABLE[base64Byte]; if (decodeByte > -1) { @@ -156,19 +158,5 @@ public class Base64Decoder { // padding if reached max position return PADDING; } - - /** - * int包装,使之可变 - * - * @author looly - * - */ - private static class IntWrapper { - int value; - - IntWrapper(int value) { - this.value = value; - } - } // ----------------------------------------------------------------------------------------------- Private end } diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/PunyCode.java b/hutool-core/src/main/java/cn/hutool/core/codec/PunyCode.java index d49a046c4..fd3fc3864 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/PunyCode.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/PunyCode.java @@ -27,11 +27,11 @@ public class PunyCode { /** * 将内容编码为PunyCode * - * @param input 字符串 + * @param input 字符串 * @return PunyCode字符串 * @throws UtilException 计算异常 */ - public static String encode(String input) throws UtilException { + public static String encode(CharSequence input) throws UtilException { return encode(input, false); } @@ -43,7 +43,7 @@ public class PunyCode { * @return PunyCode字符串 * @throws UtilException 计算异常 */ - public static String encode(String input, boolean withPrefix) throws UtilException { + public static String encode(CharSequence input, boolean withPrefix) throws UtilException { int n = INITIAL_N; int delta = 0; int bias = INITIAL_BIAS; @@ -112,7 +112,7 @@ public class PunyCode { n++; } - if(withPrefix){ + if (withPrefix) { output.insert(0, PUNY_CODE_PREFIX); } return output.toString(); @@ -214,6 +214,7 @@ public class PunyCode { * ... * 35 -> '9' * + * * @param d 输入字符 * @return 转换后的字符 * @throws UtilException 无效字符 @@ -242,6 +243,7 @@ public class PunyCode { * ... * '9' -> 35 * + * * @param c 输入字符 * @return 转换后的字符 * @throws UtilException 无效字符 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java index dce1dd9fc..ae033437f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java @@ -152,12 +152,12 @@ public class MutableInt extends Number implements Comparable, Mutabl * 相等需同时满足如下条件: *
    *
  1. 非空
  2. - *
  3. 类型为 {@link MutableInt}
  4. + *
  5. 类型为 MutableInt
  6. *
  7. 值相等
  8. *
* * @param obj 比对的对象 - * @return 相同返回true,否则 false + * @return 相同返回true,否则 {@code false} */ @Override public boolean equals(final Object obj) { @@ -176,7 +176,7 @@ public class MutableInt extends Number implements Comparable, Mutabl /** * 比较 * - * @param other 其它 {@link MutableInt} 对象 + * @param other 其它 MutableInt 对象 * @return x==y返回0,x<y返回-1,x>y返回1 */ @Override diff --git a/hutool-core/src/test/java/cn/hutool/core/codec/Base32Test.java b/hutool-core/src/test/java/cn/hutool/core/codec/Base32Test.java index a17f2e4ab..5d74654bf 100644 --- a/hutool-core/src/test/java/cn/hutool/core/codec/Base32Test.java +++ b/hutool-core/src/test/java/cn/hutool/core/codec/Base32Test.java @@ -1,6 +1,7 @@ package cn.hutool.core.codec; import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; import org.junit.Assert; import org.junit.Test; @@ -14,6 +15,24 @@ public class Base32Test { String decodeStr = Base32.decodeStr(encode); Assert.assertEquals(a, decodeStr); + + // 支持小写模式解码 + decodeStr = Base32.decodeStr(encode.toLowerCase()); + Assert.assertEquals(a, decodeStr); + } + + @Test + public void hexEncodeAndDecodeTest(){ + String a = "伦家是一个非常长的字符串"; + String encode = Base32.encodeHex(StrUtil.utf8Bytes(a)); + Assert.assertEquals("SIUADPDEMRJ9HBV4N20E9E5AT6EPTPDON3KPBFV7JA2EBBCNSUMADP5OM8======", encode); + + String decodeStr = Base32.decodeStrHex(encode); + Assert.assertEquals(a, decodeStr); + + // 支持小写模式解码 + decodeStr = Base32.decodeStrHex(encode.toLowerCase()); + Assert.assertEquals(a, decodeStr); } @Test