mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
fix BaseN
This commit is contained in:
parent
ca5732e6df
commit
3e7dd16c43
@ -9,7 +9,8 @@
|
|||||||
* 【json 】 【可能兼容问题】修改JSONObject结构,继承自MapWrapper
|
* 【json 】 【可能兼容问题】修改JSONObject结构,继承自MapWrapper
|
||||||
* 【core 】 【可能兼容问题】BeanCopier重构,新建XXXCopier,删除XXXValueProvider
|
* 【core 】 【可能兼容问题】BeanCopier重构,新建XXXCopier,删除XXXValueProvider
|
||||||
* 【core 】 【可能兼容问题】URLEncoder废弃,URLEncoderUtil使用RFC3986
|
* 【core 】 【可能兼容问题】URLEncoder废弃,URLEncoderUtil使用RFC3986
|
||||||
* 【core 】 【可能兼容问题】增加Base32.encode不足位数补=
|
* 【core 】 【可能兼容问题】Base32分离编码和解码,以便减少数据加载,支持Hex模式
|
||||||
|
* 【core 】 【不兼容问题】PunyCode参数由String改为Charsequence
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
||||||
|
@ -6,85 +6,28 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base32 - encodes and decodes RFC3548 Base32 (see http://www.faqs.org/rfcs/rfc3548.html )<br>
|
* Base32 - encodes and decodes RFC4648 Base32 (see https://datatracker.ietf.org/doc/html/rfc4648#section-6 )<br>
|
||||||
* base32就是用32(2的5次方)个特定ASCII码来表示256个ASCII码。<br>
|
* base32就是用32(2的5次方)个特定ASCII码来表示256个ASCII码。<br>
|
||||||
* 所以,5个ASCII字符经过base32编码后会变为8个字符(公约数为40),长度增加3/5.不足8n用“=”补足。
|
* 所以,5个ASCII字符经过base32编码后会变为8个字符(公约数为40),长度增加3/5.不足8n用“=”补足。<br>
|
||||||
* see http://blog.csdn.net/earbao/article/details/44453937
|
* 根据RFC4648 Base32规范,支持两种模式:
|
||||||
* @author Looly
|
* <ul>
|
||||||
|
* <li>Base 32 Alphabet (ABCDEFGHIJKLMNOPQRSTUVWXYZ234567)</li>
|
||||||
|
* <li>"Extended Hex" Base 32 Alphabet (0123456789ABCDEFGHIJKLMNOPQRSTUV)</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public class Base32 {
|
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
|
//----------------------------------------------------------------------------------------- encode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编码
|
* 编码
|
||||||
|
*
|
||||||
* @param bytes 数据
|
* @param bytes 数据
|
||||||
* @return base32
|
* @return base32
|
||||||
*/
|
*/
|
||||||
public static String encode(final byte[] bytes) {
|
public static String encode(final byte[] bytes) {
|
||||||
int i = 0;
|
return Base32Codec.INSTANCE.encode(bytes);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,18 +43,7 @@ public class Base32 {
|
|||||||
/**
|
/**
|
||||||
* base32编码
|
* base32编码
|
||||||
*
|
*
|
||||||
* @param source 被编码的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 charset 字符集
|
* @param charset 字符集
|
||||||
* @return 被加密后的字符串
|
* @return 被加密后的字符串
|
||||||
*/
|
*/
|
||||||
@ -119,55 +51,47 @@ public class Base32 {
|
|||||||
return encode(StrUtil.bytes(source, charset));
|
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
|
//----------------------------------------------------------------------------------------- decode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解码
|
* 解码
|
||||||
|
*
|
||||||
* @param base32 base32编码
|
* @param base32 base32编码
|
||||||
* @return 数据
|
* @return 数据
|
||||||
*/
|
*/
|
||||||
public static byte[] decode(final String base32) {
|
public static byte[] decode(String base32) {
|
||||||
int i, index, lookup, offset, digit;
|
return Base32Codec.INSTANCE.decode(base32);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,22 +107,42 @@ public class Base32 {
|
|||||||
/**
|
/**
|
||||||
* base32解码
|
* base32解码
|
||||||
*
|
*
|
||||||
* @param source 被解码的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 charset 字符集
|
* @param charset 字符集
|
||||||
* @return 被加密后的字符串
|
* @return 被加密后的字符串
|
||||||
*/
|
*/
|
||||||
public static String decodeStr(String source, Charset charset) {
|
public static String decodeStr(String source, Charset charset) {
|
||||||
return StrUtil.str(decode(source), 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
215
hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java
Executable file
215
hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java
Executable file
@ -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 )<br>
|
||||||
|
* base32就是用32(2的5次方)个特定ASCII码来表示256个ASCII码。<br>
|
||||||
|
* 所以,5个ASCII字符经过base32编码后会变为8个字符(公约数为40),长度增加3/5.不足8n用“=”补足。<br>
|
||||||
|
* 根据RFC4648 Base32规范,支持两种模式:
|
||||||
|
* <ul>
|
||||||
|
* <li>Base 32 Alphabet (ABCDEFGHIJKLMNOPQRSTUVWXYZ234567)</li>
|
||||||
|
* <li>"Extended Hex" Base 32 Alphabet (0123456789ABCDEFGHIJKLMNOPQRSTUV)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class Base32Codec implements Encoder<byte[], String>, Decoder<CharSequence, byte[]> {
|
||||||
|
|
||||||
|
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<byte[], String> {
|
||||||
|
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<CharSequence, byte[]> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,10 +15,6 @@ public class Base58Codec implements Encoder<byte[], String>, Decoder<CharSequenc
|
|||||||
|
|
||||||
public static Base58Codec INSTANCE = new Base58Codec();
|
public static Base58Codec INSTANCE = new Base58Codec();
|
||||||
|
|
||||||
private final char[] alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
|
|
||||||
private final char ENCODED_ZERO = alphabet[0];
|
|
||||||
private final int[] lookup = initLookup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base58编码
|
* Base58编码
|
||||||
*
|
*
|
||||||
@ -27,36 +23,7 @@ public class Base58Codec implements Encoder<byte[], String>, Decoder<CharSequenc
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String encode(byte[] data) {
|
public String encode(byte[] data) {
|
||||||
if (null == data) {
|
return Base58Encoder.ENCODER.encode(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] == ENCODED_ZERO) {
|
|
||||||
++outputStart;
|
|
||||||
}
|
|
||||||
while (--zeroCount >= 0) {
|
|
||||||
encoded[--outputStart] = ENCODED_ZERO;
|
|
||||||
}
|
|
||||||
// Return encoded string (including encoded leading zeros).
|
|
||||||
return new String(encoded, outputStart, encoded.length - outputStart);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,52 +35,130 @@ public class Base58Codec implements Encoder<byte[], String>, Decoder<CharSequenc
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public byte[] decode(CharSequence encoded) throws IllegalArgumentException {
|
public byte[] decode(CharSequence encoded) throws IllegalArgumentException {
|
||||||
if (encoded.length() == 0) {
|
return Base58Decoder.DECODER.decode(encoded);
|
||||||
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 ? lookup[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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化字符序号查找表
|
* Base58编码器
|
||||||
*
|
*
|
||||||
* @return 字符序号查找表
|
* @since 5.8.0
|
||||||
*/
|
*/
|
||||||
private int[] initLookup() {
|
public static class Base58Encoder implements Encoder<byte[], String> {
|
||||||
final int[] lookup = new int['z' + 1];
|
private static final String DEFAULT_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
Arrays.fill(lookup, -1);
|
|
||||||
for (int i = 0; i < alphabet.length; i++)
|
public static final Base58Encoder ENCODER = new Base58Encoder(DEFAULT_ALPHABET.toCharArray());
|
||||||
lookup[alphabet[i]] = i;
|
|
||||||
return lookup;
|
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<CharSequence, byte[]> {
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.hutool.core.codec;
|
package cn.hutool.core.codec;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.mutable.MutableInt;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@ -87,7 +88,7 @@ public class Base64Decoder {
|
|||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
final IntWrapper offset = new IntWrapper(pos);
|
final MutableInt offset = new MutableInt(pos);
|
||||||
|
|
||||||
byte sestet0;
|
byte sestet0;
|
||||||
byte sestet1;
|
byte sestet1;
|
||||||
@ -96,7 +97,7 @@ public class Base64Decoder {
|
|||||||
int maxPos = pos + length - 1;
|
int maxPos = pos + length - 1;
|
||||||
int octetId = 0;
|
int octetId = 0;
|
||||||
byte[] octet = new byte[length * 3 / 4];// over-estimated if non-base64 characters present
|
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);
|
sestet0 = getNextValidDecodeByte(in, offset, maxPos);
|
||||||
sestet1 = getNextValidDecodeByte(in, offset, maxPos);
|
sestet1 = getNextValidDecodeByte(in, offset, maxPos);
|
||||||
sestet2 = getNextValidDecodeByte(in, offset, maxPos);
|
sestet2 = getNextValidDecodeByte(in, offset, maxPos);
|
||||||
@ -141,11 +142,12 @@ public class Base64Decoder {
|
|||||||
* @param maxPos 最大位置
|
* @param maxPos 最大位置
|
||||||
* @return 有效字符,如果达到末尾返回
|
* @return 有效字符,如果达到末尾返回
|
||||||
*/
|
*/
|
||||||
private static byte getNextValidDecodeByte(byte[] in, IntWrapper pos, int maxPos) {
|
private static byte getNextValidDecodeByte(byte[] in, MutableInt pos, int maxPos) {
|
||||||
byte base64Byte;
|
byte base64Byte;
|
||||||
byte decodeByte;
|
byte decodeByte;
|
||||||
while (pos.value <= maxPos) {
|
while (pos.intValue() <= maxPos) {
|
||||||
base64Byte = in[pos.value++];
|
base64Byte = in[pos.intValue()];
|
||||||
|
pos.increment();
|
||||||
if (base64Byte > -1) {
|
if (base64Byte > -1) {
|
||||||
decodeByte = DECODE_TABLE[base64Byte];
|
decodeByte = DECODE_TABLE[base64Byte];
|
||||||
if (decodeByte > -1) {
|
if (decodeByte > -1) {
|
||||||
@ -156,19 +158,5 @@ public class Base64Decoder {
|
|||||||
// padding if reached max position
|
// padding if reached max position
|
||||||
return PADDING;
|
return PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* int包装,使之可变
|
|
||||||
*
|
|
||||||
* @author looly
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private static class IntWrapper {
|
|
||||||
int value;
|
|
||||||
|
|
||||||
IntWrapper(int value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ----------------------------------------------------------------------------------------------- Private end
|
// ----------------------------------------------------------------------------------------------- Private end
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,11 @@ public class PunyCode {
|
|||||||
/**
|
/**
|
||||||
* 将内容编码为PunyCode
|
* 将内容编码为PunyCode
|
||||||
*
|
*
|
||||||
* @param input 字符串
|
* @param input 字符串
|
||||||
* @return PunyCode字符串
|
* @return PunyCode字符串
|
||||||
* @throws UtilException 计算异常
|
* @throws UtilException 计算异常
|
||||||
*/
|
*/
|
||||||
public static String encode(String input) throws UtilException {
|
public static String encode(CharSequence input) throws UtilException {
|
||||||
return encode(input, false);
|
return encode(input, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ public class PunyCode {
|
|||||||
* @return PunyCode字符串
|
* @return PunyCode字符串
|
||||||
* @throws UtilException 计算异常
|
* @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 n = INITIAL_N;
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
int bias = INITIAL_BIAS;
|
int bias = INITIAL_BIAS;
|
||||||
@ -112,7 +112,7 @@ public class PunyCode {
|
|||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(withPrefix){
|
if (withPrefix) {
|
||||||
output.insert(0, PUNY_CODE_PREFIX);
|
output.insert(0, PUNY_CODE_PREFIX);
|
||||||
}
|
}
|
||||||
return output.toString();
|
return output.toString();
|
||||||
@ -214,6 +214,7 @@ public class PunyCode {
|
|||||||
* ...
|
* ...
|
||||||
* 35 -> '9'
|
* 35 -> '9'
|
||||||
* </pre>
|
* </pre>
|
||||||
|
*
|
||||||
* @param d 输入字符
|
* @param d 输入字符
|
||||||
* @return 转换后的字符
|
* @return 转换后的字符
|
||||||
* @throws UtilException 无效字符
|
* @throws UtilException 无效字符
|
||||||
@ -242,6 +243,7 @@ public class PunyCode {
|
|||||||
* ...
|
* ...
|
||||||
* '9' -> 35
|
* '9' -> 35
|
||||||
* </pre>
|
* </pre>
|
||||||
|
*
|
||||||
* @param c 输入字符
|
* @param c 输入字符
|
||||||
* @return 转换后的字符
|
* @return 转换后的字符
|
||||||
* @throws UtilException 无效字符
|
* @throws UtilException 无效字符
|
||||||
|
@ -152,12 +152,12 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
|
|||||||
* 相等需同时满足如下条件:
|
* 相等需同时满足如下条件:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>非空</li>
|
* <li>非空</li>
|
||||||
* <li>类型为 {@link MutableInt}</li>
|
* <li>类型为 MutableInt</li>
|
||||||
* <li>值相等</li>
|
* <li>值相等</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @param obj 比对的对象
|
* @param obj 比对的对象
|
||||||
* @return 相同返回<code>true</code>,否则 <code>false</code>
|
* @return 相同返回<code>true</code>,否则 {@code false}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
@ -176,7 +176,7 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
|
|||||||
/**
|
/**
|
||||||
* 比较
|
* 比较
|
||||||
*
|
*
|
||||||
* @param other 其它 {@link MutableInt} 对象
|
* @param other 其它 MutableInt 对象
|
||||||
* @return x==y返回0,x<y返回-1,x>y返回1
|
* @return x==y返回0,x<y返回-1,x>y返回1
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.hutool.core.codec;
|
package cn.hutool.core.codec;
|
||||||
|
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -14,6 +15,24 @@ public class Base32Test {
|
|||||||
|
|
||||||
String decodeStr = Base32.decodeStr(encode);
|
String decodeStr = Base32.decodeStr(encode);
|
||||||
Assert.assertEquals(a, decodeStr);
|
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
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user