Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ma yanling
hutool-5-master
Commits
45cda665
Commit
45cda665
authored
Sep 25, 2024
by
ma yanling
Browse files
project commit
parent
ad2fb30a
Pipeline
#2354
failed with stages
in 0 seconds
Changes
369
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
3084 additions
and
0 deletions
+3084
-0
hutool-core/src/main/java/cn/hutool/core/clone/CloneSupport.java
...core/src/main/java/cn/hutool/core/clone/CloneSupport.java
+21
-0
hutool-core/src/main/java/cn/hutool/core/clone/Cloneable.java
...ol-core/src/main/java/cn/hutool/core/clone/Cloneable.java
+16
-0
hutool-core/src/main/java/cn/hutool/core/clone/DefaultCloneable.java
.../src/main/java/cn/hutool/core/clone/DefaultCloneable.java
+28
-0
hutool-core/src/main/java/cn/hutool/core/clone/package-info.java
...core/src/main/java/cn/hutool/core/clone/package-info.java
+7
-0
hutool-core/src/main/java/cn/hutool/core/codec/BCD.java
hutool-core/src/main/java/cn/hutool/core/codec/BCD.java
+129
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base16Codec.java
...-core/src/main/java/cn/hutool/core/codec/Base16Codec.java
+118
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base32.java
hutool-core/src/main/java/cn/hutool/core/codec/Base32.java
+148
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java
...-core/src/main/java/cn/hutool/core/codec/Base32Codec.java
+215
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base58.java
hutool-core/src/main/java/cn/hutool/core/codec/Base58.java
+152
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java
...-core/src/main/java/cn/hutool/core/codec/Base58Codec.java
+187
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base62.java
hutool-core/src/main/java/cn/hutool/core/codec/Base62.java
+262
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base62Codec.java
...-core/src/main/java/cn/hutool/core/codec/Base62Codec.java
+232
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base64.java
hutool-core/src/main/java/cn/hutool/core/codec/Base64.java
+386
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base64Decoder.java
...ore/src/main/java/cn/hutool/core/codec/Base64Decoder.java
+162
-0
hutool-core/src/main/java/cn/hutool/core/codec/Base64Encoder.java
...ore/src/main/java/cn/hutool/core/codec/Base64Encoder.java
+214
-0
hutool-core/src/main/java/cn/hutool/core/codec/Caesar.java
hutool-core/src/main/java/cn/hutool/core/codec/Caesar.java
+89
-0
hutool-core/src/main/java/cn/hutool/core/codec/Decoder.java
hutool-core/src/main/java/cn/hutool/core/codec/Decoder.java
+20
-0
hutool-core/src/main/java/cn/hutool/core/codec/Encoder.java
hutool-core/src/main/java/cn/hutool/core/codec/Encoder.java
+20
-0
hutool-core/src/main/java/cn/hutool/core/codec/Hashids.java
hutool-core/src/main/java/cn/hutool/core/codec/Hashids.java
+506
-0
hutool-core/src/main/java/cn/hutool/core/codec/Morse.java
hutool-core/src/main/java/cn/hutool/core/codec/Morse.java
+172
-0
No files found.
Too many changes to show.
To preserve performance only
369 of 369+
files are displayed.
Plain diff
Email patch
hutool-core/src/main/java/cn/hutool/core/clone/CloneSupport.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.clone
;
/**
* 克隆支持类,提供默认的克隆方法
* @author Looly
*
* @param <T> 继承类的类型
*/
public
class
CloneSupport
<
T
>
implements
Cloneable
<
T
>{
@SuppressWarnings
(
"unchecked"
)
@Override
public
T
clone
()
{
try
{
return
(
T
)
super
.
clone
();
}
catch
(
CloneNotSupportedException
e
)
{
throw
new
CloneRuntimeException
(
e
);
}
}
}
hutool-core/src/main/java/cn/hutool/core/clone/Cloneable.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.clone
;
/**
* 克隆支持接口
* @author Looly
*
* @param <T> 实现克隆接口的类型
*/
public
interface
Cloneable
<
T
>
extends
java
.
lang
.
Cloneable
{
/**
* 克隆当前对象,浅复制
* @return 克隆后的对象
*/
T
clone
();
}
hutool-core/src/main/java/cn/hutool/core/clone/DefaultCloneable.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.clone
;
import
cn.hutool.core.util.ReflectUtil
;
/**
* 克隆默认实现接口,用于实现返回指定泛型类型的克隆方法
*
* @param <T> 泛型类型
* @since 5.7.17
*/
public
interface
DefaultCloneable
<
T
>
extends
java
.
lang
.
Cloneable
{
/**
* 浅拷贝,提供默认的泛型返回值的clone方法。
*
* @return obj
*/
default
T
clone0
()
{
try
{
return
ReflectUtil
.
invoke
(
this
,
"clone"
);
}
catch
(
Exception
e
)
{
throw
new
CloneRuntimeException
(
e
);
}
}
}
hutool-core/src/main/java/cn/hutool/core/clone/package-info.java
0 → 100644
View file @
45cda665
/**
* 克隆封装
*
* @author looly
*
*/
package
cn.hutool.core.clone
;
\ No newline at end of file
hutool-core/src/main/java/cn/hutool/core/codec/BCD.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.lang.Assert
;
/**
* BCD码(Binary-Coded Decimal)亦称二进码十进数或二-十进制代码<br>
* BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷的进行<br>
* see http://cuisuqiang.iteye.com/blog/1429956
* @author Looly
*
* @deprecated 由于对于ASCII的编码解码有缺陷,且这种BCD实现并不规范,因此会在6.0.0中移除
*/
@Deprecated
public
class
BCD
{
/**
* 字符串转BCD码
* @param asc ASCII字符串
* @return BCD
*/
public
static
byte
[]
strToBcd
(
String
asc
)
{
Assert
.
notNull
(
asc
,
"ASCII must not be null!"
);
int
len
=
asc
.
length
();
int
mod
=
len
%
2
;
if
(
mod
!=
0
)
{
asc
=
"0"
+
asc
;
len
=
asc
.
length
();
}
byte
[]
abt
;
if
(
len
>=
2
)
{
len
>>=
1
;
}
byte
[]
bbt
;
bbt
=
new
byte
[
len
];
abt
=
asc
.
getBytes
();
int
j
;
int
k
;
for
(
int
p
=
0
;
p
<
asc
.
length
()
/
2
;
p
++)
{
if
((
abt
[
2
*
p
]
>=
'0'
)
&&
(
abt
[
2
*
p
]
<=
'9'
))
{
j
=
abt
[
2
*
p
]
-
'0'
;
}
else
if
((
abt
[
2
*
p
]
>=
'a'
)
&&
(
abt
[
2
*
p
]
<=
'z'
))
{
j
=
abt
[
2
*
p
]
-
'a'
+
0x0a
;
}
else
{
j
=
abt
[
2
*
p
]
-
'A'
+
0x0a
;
}
if
((
abt
[
2
*
p
+
1
]
>=
'0'
)
&&
(
abt
[
2
*
p
+
1
]
<=
'9'
))
{
k
=
abt
[
2
*
p
+
1
]
-
'0'
;
}
else
if
((
abt
[
2
*
p
+
1
]
>=
'a'
)
&&
(
abt
[
2
*
p
+
1
]
<=
'z'
))
{
k
=
abt
[
2
*
p
+
1
]
-
'a'
+
0x0a
;
}
else
{
k
=
abt
[
2
*
p
+
1
]
-
'A'
+
0x0a
;
}
int
a
=
(
j
<<
4
)
+
k
;
byte
b
=
(
byte
)
a
;
bbt
[
p
]
=
b
;
}
return
bbt
;
}
/**
* ASCII转BCD
* @param ascii ASCII byte数组
* @return BCD
*/
public
static
byte
[]
ascToBcd
(
byte
[]
ascii
)
{
Assert
.
notNull
(
ascii
,
"Ascii must be not null!"
);
return
ascToBcd
(
ascii
,
ascii
.
length
);
}
/**
* ASCII转BCD
* @param ascii ASCII byte数组
* @param ascLength 长度
* @return BCD
*/
public
static
byte
[]
ascToBcd
(
byte
[]
ascii
,
int
ascLength
)
{
Assert
.
notNull
(
ascii
,
"Ascii must be not null!"
);
byte
[]
bcd
=
new
byte
[
ascLength
/
2
];
int
j
=
0
;
for
(
int
i
=
0
;
i
<
(
ascLength
+
1
)
/
2
;
i
++)
{
bcd
[
i
]
=
ascToBcd
(
ascii
[
j
++]);
bcd
[
i
]
=
(
byte
)
(((
j
>=
ascLength
)
?
0x00
:
ascToBcd
(
ascii
[
j
++]))
+
(
bcd
[
i
]
<<
4
));
}
return
bcd
;
}
/**
* BCD转ASCII字符串
* @param bytes BCD byte数组
* @return ASCII字符串
*/
public
static
String
bcdToStr
(
byte
[]
bytes
)
{
Assert
.
notNull
(
bytes
,
"Bcd bytes must be not null!"
);
char
[]
temp
=
new
char
[
bytes
.
length
*
2
];
char
val
;
for
(
int
i
=
0
;
i
<
bytes
.
length
;
i
++)
{
val
=
(
char
)
(((
bytes
[
i
]
&
0xf0
)
>>
4
)
&
0x0f
);
temp
[
i
*
2
]
=
(
char
)
(
val
>
9
?
val
+
'A'
-
10
:
val
+
'0'
);
val
=
(
char
)
(
bytes
[
i
]
&
0x0f
);
temp
[
i
*
2
+
1
]
=
(
char
)
(
val
>
9
?
val
+
'A'
-
10
:
val
+
'0'
);
}
return
new
String
(
temp
);
}
//----------------------------------------------------------------- Private method start
/**
* 转换单个byte为BCD
* @param asc ACSII
* @return BCD
*/
private
static
byte
ascToBcd
(
byte
asc
)
{
byte
bcd
;
if
((
asc
>=
'0'
)
&&
(
asc
<=
'9'
))
{
bcd
=
(
byte
)
(
asc
-
'0'
);
}
else
if
((
asc
>=
'A'
)
&&
(
asc
<=
'F'
))
{
bcd
=
(
byte
)
(
asc
-
'A'
+
10
);
}
else
if
((
asc
>=
'a'
)
&&
(
asc
<=
'f'
))
{
bcd
=
(
byte
)
(
asc
-
'a'
+
10
);
}
else
{
bcd
=
(
byte
)
(
asc
-
48
);
}
return
bcd
;
}
//----------------------------------------------------------------- Private method end
}
hutool-core/src/main/java/cn/hutool/core/codec/Base16Codec.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.exceptions.UtilException
;
import
cn.hutool.core.util.StrUtil
;
/**
* Base16(Hex)编码解码器<br>
* 十六进制(简写为hex或下标16)在数学中是一种逢16进1的进位制,一般用数字0到9和字母A到F表示(其中:A~F即10~15)。<br>
* 例如十进制数57,在二进制写作111001,在16进制写作39。
*
* @author looly
* @since 5.7.23
*/
public
class
Base16Codec
implements
Encoder
<
byte
[],
char
[]>,
Decoder
<
CharSequence
,
byte
[]>
{
public
static
final
Base16Codec
CODEC_LOWER
=
new
Base16Codec
(
true
);
public
static
final
Base16Codec
CODEC_UPPER
=
new
Base16Codec
(
false
);
private
final
char
[]
alphabets
;
/**
* 构造
*
* @param lowerCase 是否小写
*/
public
Base16Codec
(
boolean
lowerCase
)
{
this
.
alphabets
=
(
lowerCase
?
"0123456789abcdef"
:
"0123456789ABCDEF"
).
toCharArray
();
}
@Override
public
char
[]
encode
(
byte
[]
data
)
{
final
int
len
=
data
.
length
;
final
char
[]
out
=
new
char
[
len
<<
1
];
//len*2
// two characters from the hex value.
for
(
int
i
=
0
,
j
=
0
;
i
<
len
;
i
++)
{
out
[
j
++]
=
alphabets
[(
0xF0
&
data
[
i
])
>>>
4
];
// 高位
out
[
j
++]
=
alphabets
[
0x0F
&
data
[
i
]];
// 低位
}
return
out
;
}
@Override
public
byte
[]
decode
(
CharSequence
encoded
)
{
if
(
StrUtil
.
isEmpty
(
encoded
))
{
return
null
;
}
encoded
=
StrUtil
.
cleanBlank
(
encoded
);
int
len
=
encoded
.
length
();
if
((
len
&
0x01
)
!=
0
)
{
// 如果提供的数据是奇数长度,则前面补0凑偶数
encoded
=
"0"
+
encoded
;
len
=
encoded
.
length
();
}
final
byte
[]
out
=
new
byte
[
len
>>
1
];
// two characters form the hex value.
for
(
int
i
=
0
,
j
=
0
;
j
<
len
;
i
++)
{
int
f
=
toDigit
(
encoded
.
charAt
(
j
),
j
)
<<
4
;
j
++;
f
=
f
|
toDigit
(
encoded
.
charAt
(
j
),
j
);
j
++;
out
[
i
]
=
(
byte
)
(
f
&
0xFF
);
}
return
out
;
}
/**
* 将指定char值转换为Unicode字符串形式,常用于特殊字符(例如汉字)转Unicode形式<br>
* 转换的字符串如果u后不足4位,则前面用0填充,例如:
*
* <pre>
* '你' =》'\u4f60'
* </pre>
*
* @param ch char值
* @return Unicode表现形式
*/
public
String
toUnicodeHex
(
char
ch
)
{
return
"\\u"
+
//
alphabets
[(
ch
>>
12
)
&
15
]
+
//
alphabets
[(
ch
>>
8
)
&
15
]
+
//
alphabets
[(
ch
>>
4
)
&
15
]
+
//
alphabets
[(
ch
)
&
15
];
}
/**
* 将byte值转为16进制并添加到{@link StringBuilder}中
*
* @param builder {@link StringBuilder}
* @param b byte
*/
public
void
appendHex
(
StringBuilder
builder
,
byte
b
)
{
int
high
=
(
b
&
0xf0
)
>>>
4
;
//高位
int
low
=
b
&
0x0f
;
//低位
builder
.
append
(
alphabets
[
high
]);
builder
.
append
(
alphabets
[
low
]);
}
/**
* 将十六进制字符转换成一个整数
*
* @param ch 十六进制char
* @param index 十六进制字符在字符数组中的位置
* @return 一个整数
* @throws UtilException 当ch不是一个合法的十六进制字符时,抛出运行时异常
*/
private
static
int
toDigit
(
char
ch
,
int
index
)
{
int
digit
=
Character
.
digit
(
ch
,
16
);
if
(
digit
<
0
)
{
throw
new
UtilException
(
"Illegal hexadecimal character {} at index {}"
,
ch
,
index
);
}
return
digit
;
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base32.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.util.CharsetUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.nio.charset.Charset
;
/**
* 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
*/
public
class
Base32
{
//----------------------------------------------------------------------------------------- encode
/**
* 编码
*
* @param bytes 数据
* @return base32
*/
public
static
String
encode
(
final
byte
[]
bytes
)
{
return
Base32Codec
.
INSTANCE
.
encode
(
bytes
);
}
/**
* base32编码
*
* @param source 被编码的base32字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
String
source
)
{
return
encode
(
source
,
CharsetUtil
.
CHARSET_UTF_8
);
}
/**
* base32编码
*
* @param source 被编码的base32字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
encode
(
String
source
,
Charset
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
/**
* 解码
*
* @param base32 base32编码
* @return 数据
*/
public
static
byte
[]
decode
(
String
base32
)
{
return
Base32Codec
.
INSTANCE
.
decode
(
base32
);
}
/**
* base32解码
*
* @param source 被解码的base32字符串
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
String
source
)
{
return
decodeStr
(
source
,
CharsetUtil
.
CHARSET_UTF_8
);
}
/**
* 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
);
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base32Codec.java
0 → 100644
View file @
45cda665
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
;
}
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base58.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.exceptions.UtilException
;
import
cn.hutool.core.exceptions.ValidateException
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
import
java.util.Arrays
;
/**
* Base58工具类,提供Base58的编码和解码方案<br>
* 参考: https://github.com/Anujraval24/Base58Encoding<br>
* 规范见:https://en.bitcoin.it/wiki/Base58Check_encoding
*
* @author lin, looly
* @since 5.7.22
*/
public
class
Base58
{
private
static
final
int
CHECKSUM_SIZE
=
4
;
// -------------------------------------------------------------------- encode
/**
* Base58编码<br>
* 包含版本位和校验位
*
* @param version 编码版本,{@code null}表示不包含版本位
* @param data 被编码的数组,添加校验和。
* @return 编码后的字符串
*/
public
static
String
encodeChecked
(
Integer
version
,
byte
[]
data
)
{
return
encode
(
addChecksum
(
version
,
data
));
}
/**
* Base58编码
*
* @param data 被编码的数据,不带校验和。
* @return 编码后的字符串
*/
public
static
String
encode
(
byte
[]
data
)
{
return
Base58Codec
.
INSTANCE
.
encode
(
data
);
}
// -------------------------------------------------------------------- decode
/**
* Base58解码<br>
* 解码包含标志位验证和版本呢位去除
*
* @param encoded 被解码的base58字符串
* @return 解码后的bytes
* @throws ValidateException 标志位验证错误抛出此异常
*/
public
static
byte
[]
decodeChecked
(
CharSequence
encoded
)
throws
ValidateException
{
try
{
return
decodeChecked
(
encoded
,
true
);
}
catch
(
ValidateException
ignore
)
{
return
decodeChecked
(
encoded
,
false
);
}
}
/**
* Base58解码<br>
* 解码包含标志位验证和版本呢位去除
*
* @param encoded 被解码的base58字符串
* @param withVersion 是否包含版本位
* @return 解码后的bytes
* @throws ValidateException 标志位验证错误抛出此异常
*/
public
static
byte
[]
decodeChecked
(
CharSequence
encoded
,
boolean
withVersion
)
throws
ValidateException
{
byte
[]
valueWithChecksum
=
decode
(
encoded
);
return
verifyAndRemoveChecksum
(
valueWithChecksum
,
withVersion
);
}
/**
* Base58解码
*
* @param encoded 被编码的base58字符串
* @return 解码后的bytes
*/
public
static
byte
[]
decode
(
CharSequence
encoded
)
{
return
Base58Codec
.
INSTANCE
.
decode
(
encoded
);
}
/**
* 验证并去除验证位和版本位
*
* @param data 编码的数据
* @param withVersion 是否包含版本位
* @return 载荷数据
*/
private
static
byte
[]
verifyAndRemoveChecksum
(
byte
[]
data
,
boolean
withVersion
)
{
final
byte
[]
payload
=
Arrays
.
copyOfRange
(
data
,
withVersion
?
1
:
0
,
data
.
length
-
CHECKSUM_SIZE
);
final
byte
[]
checksum
=
Arrays
.
copyOfRange
(
data
,
data
.
length
-
CHECKSUM_SIZE
,
data
.
length
);
final
byte
[]
expectedChecksum
=
checksum
(
payload
);
if
(
false
==
Arrays
.
equals
(
checksum
,
expectedChecksum
))
{
throw
new
ValidateException
(
"Base58 checksum is invalid"
);
}
return
payload
;
}
/**
* 数据 + 校验码
*
* @param version 版本,{@code null}表示不添加版本位
* @param payload Base58数据(不含校验码)
* @return Base58数据
*/
private
static
byte
[]
addChecksum
(
Integer
version
,
byte
[]
payload
)
{
final
byte
[]
addressBytes
;
if
(
null
!=
version
)
{
addressBytes
=
new
byte
[
1
+
payload
.
length
+
CHECKSUM_SIZE
];
addressBytes
[
0
]
=
(
byte
)
version
.
intValue
();
System
.
arraycopy
(
payload
,
0
,
addressBytes
,
1
,
payload
.
length
);
}
else
{
addressBytes
=
new
byte
[
payload
.
length
+
CHECKSUM_SIZE
];
System
.
arraycopy
(
payload
,
0
,
addressBytes
,
0
,
payload
.
length
);
}
final
byte
[]
checksum
=
checksum
(
payload
);
System
.
arraycopy
(
checksum
,
0
,
addressBytes
,
addressBytes
.
length
-
CHECKSUM_SIZE
,
CHECKSUM_SIZE
);
return
addressBytes
;
}
/**
* 获取校验码<br>
* 计算规则为对数据进行两次sha256计算,然后取{@link #CHECKSUM_SIZE}长度
*
* @param data 数据
* @return 校验码
*/
private
static
byte
[]
checksum
(
byte
[]
data
)
{
byte
[]
hash
=
hash256
(
hash256
(
data
));
return
Arrays
.
copyOfRange
(
hash
,
0
,
CHECKSUM_SIZE
);
}
/**
* 计算数据的SHA-256值
*
* @param data 数据
* @return sha-256值
*/
private
static
byte
[]
hash256
(
byte
[]
data
)
{
try
{
return
MessageDigest
.
getInstance
(
"SHA-256"
).
digest
(
data
);
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
UtilException
(
e
);
}
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.util.StrUtil
;
import
java.util.Arrays
;
/**
* Base58编码器<br>
* 此编码器不包括校验码、版本等信息
*
* @author lin, looly
* @since 5.7.22
*/
public
class
Base58Codec
implements
Encoder
<
byte
[],
String
>,
Decoder
<
CharSequence
,
byte
[]>
{
public
static
Base58Codec
INSTANCE
=
new
Base58Codec
();
/**
* Base58编码
*
* @param data 被编码的数据,不带校验和。
* @return 编码后的字符串
*/
@Override
public
String
encode
(
byte
[]
data
)
{
return
Base58Encoder
.
ENCODER
.
encode
(
data
);
}
/**
* 解码给定的Base58字符串
*
* @param encoded Base58编码字符串
* @return 解码后的bytes
* @throws IllegalArgumentException 非标准Base58字符串
*/
@Override
public
byte
[]
decode
(
CharSequence
encoded
)
throws
IllegalArgumentException
{
return
Base58Decoder
.
DECODER
.
decode
(
encoded
);
}
/**
* Base58编码器
*
* @since 5.8.0
*/
public
static
class
Base58Encoder
implements
Encoder
<
byte
[],
String
>
{
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
<
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
);
}
}
/**
* Divides a number, represented as an array of bytes each containing a single digit
* in the specified base, by the given divisor. The given number is modified in-place
* to contain the quotient, and the return value is the remainder.
*
* @param number the number to divide
* @param firstDigit the index within the array of the first non-zero digit
* (this is used for optimization by skipping the leading zeros)
* @param base the base in which the number's digits are represented (up to 256)
* @param divisor the number to divide by (up to 256)
* @return the remainder of the division operation
*/
private
static
byte
divmod
(
byte
[]
number
,
int
firstDigit
,
int
base
,
int
divisor
)
{
// this is just long division which accounts for the base of the input digits
int
remainder
=
0
;
for
(
int
i
=
firstDigit
;
i
<
number
.
length
;
i
++)
{
int
digit
=
(
int
)
number
[
i
]
&
0xFF
;
int
temp
=
remainder
*
base
+
digit
;
number
[
i
]
=
(
byte
)
(
temp
/
divisor
);
remainder
=
temp
%
divisor
;
}
return
(
byte
)
remainder
;
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base62.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.io.IoUtil
;
import
cn.hutool.core.util.CharsetUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.io.File
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.nio.charset.Charset
;
/**
* Base62工具类,提供Base62的编码和解码方案<br>
*
* @author Looly
* @since 4.5.9
*/
public
class
Base62
{
private
static
final
Charset
DEFAULT_CHARSET
=
CharsetUtil
.
CHARSET_UTF_8
;
// -------------------------------------------------------------------- encode
/**
* Base62编码
*
* @param source 被编码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
CharSequence
source
)
{
return
encode
(
source
,
DEFAULT_CHARSET
);
}
/**
* Base62编码
*
* @param source 被编码的Base62字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
encode
(
CharSequence
source
,
Charset
charset
)
{
return
encode
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* Base62编码
*
* @param source 被编码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
byte
[]
source
)
{
return
new
String
(
Base62Codec
.
INSTANCE
.
encode
(
source
));
}
/**
* Base62编码
*
* @param in 被编码Base62的流(一般为图片流或者文件流)
* @return 被加密后的字符串
*/
public
static
String
encode
(
InputStream
in
)
{
return
encode
(
IoUtil
.
readBytes
(
in
));
}
/**
* Base62编码
*
* @param file 被编码Base62的文件
* @return 被加密后的字符串
*/
public
static
String
encode
(
File
file
)
{
return
encode
(
FileUtil
.
readBytes
(
file
));
}
/**
* Base62编码(反转字母表模式)
*
* @param source 被编码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
encodeInverted
(
CharSequence
source
)
{
return
encodeInverted
(
source
,
DEFAULT_CHARSET
);
}
/**
* Base62编码(反转字母表模式)
*
* @param source 被编码的Base62字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
encodeInverted
(
CharSequence
source
,
Charset
charset
)
{
return
encodeInverted
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* Base62编码(反转字母表模式)
*
* @param source 被编码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
encodeInverted
(
byte
[]
source
)
{
return
new
String
(
Base62Codec
.
INSTANCE
.
encode
(
source
,
true
));
}
/**
* Base62编码
*
* @param in 被编码Base62的流(一般为图片流或者文件流)
* @return 被加密后的字符串
*/
public
static
String
encodeInverted
(
InputStream
in
)
{
return
encodeInverted
(
IoUtil
.
readBytes
(
in
));
}
/**
* Base62编码(反转字母表模式)
*
* @param file 被编码Base62的文件
* @return 被加密后的字符串
*/
public
static
String
encodeInverted
(
File
file
)
{
return
encodeInverted
(
FileUtil
.
readBytes
(
file
));
}
// -------------------------------------------------------------------- decode
/**
* Base62解码
*
* @param source 被解码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
decodeStrGbk
(
CharSequence
source
)
{
return
decodeStr
(
source
,
CharsetUtil
.
CHARSET_GBK
);
}
/**
* Base62解码
*
* @param source 被解码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
)
{
return
decodeStr
(
source
,
DEFAULT_CHARSET
);
}
/**
* Base62解码
*
* @param source 被解码的Base62字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
,
Charset
charset
)
{
return
StrUtil
.
str
(
decode
(
source
),
charset
);
}
/**
* Base62解码
*
* @param Base62 被解码的Base62字符串
* @param destFile 目标文件
* @return 目标文件
*/
public
static
File
decodeToFile
(
CharSequence
Base62
,
File
destFile
)
{
return
FileUtil
.
writeBytes
(
decode
(
Base62
),
destFile
);
}
/**
* Base62解码
*
* @param base62Str 被解码的Base62字符串
* @param out 写出到的流
* @param isCloseOut 是否关闭输出流
*/
public
static
void
decodeToStream
(
CharSequence
base62Str
,
OutputStream
out
,
boolean
isCloseOut
)
{
IoUtil
.
write
(
out
,
isCloseOut
,
decode
(
base62Str
));
}
/**
* Base62解码
*
* @param base62Str 被解码的Base62字符串
* @return 被加密后的字符串
*/
public
static
byte
[]
decode
(
CharSequence
base62Str
)
{
return
decode
(
StrUtil
.
bytes
(
base62Str
,
DEFAULT_CHARSET
));
}
/**
* 解码Base62
*
* @param base62bytes Base62输入
* @return 解码后的bytes
*/
public
static
byte
[]
decode
(
byte
[]
base62bytes
)
{
return
Base62Codec
.
INSTANCE
.
decode
(
base62bytes
);
}
/**
* Base62解码(反转字母表模式)
*
* @param source 被解码的Base62字符串
* @return 被加密后的字符串
*/
public
static
String
decodeStrInverted
(
CharSequence
source
)
{
return
decodeStrInverted
(
source
,
DEFAULT_CHARSET
);
}
/**
* Base62解码(反转字母表模式)
*
* @param source 被解码的Base62字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
decodeStrInverted
(
CharSequence
source
,
Charset
charset
)
{
return
StrUtil
.
str
(
decodeInverted
(
source
),
charset
);
}
/**
* Base62解码(反转字母表模式)
*
* @param Base62 被解码的Base62字符串
* @param destFile 目标文件
* @return 目标文件
*/
public
static
File
decodeToFileInverted
(
CharSequence
Base62
,
File
destFile
)
{
return
FileUtil
.
writeBytes
(
decodeInverted
(
Base62
),
destFile
);
}
/**
* Base62解码(反转字母表模式)
*
* @param base62Str 被解码的Base62字符串
* @param out 写出到的流
* @param isCloseOut 是否关闭输出流
*/
public
static
void
decodeToStreamInverted
(
CharSequence
base62Str
,
OutputStream
out
,
boolean
isCloseOut
)
{
IoUtil
.
write
(
out
,
isCloseOut
,
decodeInverted
(
base62Str
));
}
/**
* Base62解码(反转字母表模式)
*
* @param base62Str 被解码的Base62字符串
* @return 被加密后的字符串
*/
public
static
byte
[]
decodeInverted
(
CharSequence
base62Str
)
{
return
decodeInverted
(
StrUtil
.
bytes
(
base62Str
,
DEFAULT_CHARSET
));
}
/**
* 解码Base62(反转字母表模式)
*
* @param base62bytes Base62输入
* @return 解码后的bytes
*/
public
static
byte
[]
decodeInverted
(
byte
[]
base62bytes
)
{
return
Base62Codec
.
INSTANCE
.
decode
(
base62bytes
,
true
);
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base62Codec.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.util.ArrayUtil
;
import
java.io.ByteArrayOutputStream
;
import
java.io.Serializable
;
/**
* Base62编码解码实现,常用于短URL<br>
* From https://github.com/seruco/base62
*
* @author Looly, Sebastian Ruhleder, sebastian@seruco.io
* @since 4.5.9
*/
public
class
Base62Codec
implements
Encoder
<
byte
[],
byte
[]>,
Decoder
<
byte
[],
byte
[]>,
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
static
final
int
STANDARD_BASE
=
256
;
private
static
final
int
TARGET_BASE
=
62
;
public
static
Base62Codec
INSTANCE
=
new
Base62Codec
();
/**
* 编码指定消息bytes为Base62格式的bytes
*
* @param data 被编码的消息
* @return Base62内容
*/
@Override
public
byte
[]
encode
(
byte
[]
data
)
{
return
encode
(
data
,
false
);
}
/**
* 编码指定消息bytes为Base62格式的bytes
*
* @param data 被编码的消息
* @param useInverted 是否使用反转风格,即将GMP风格中的大小写做转换
* @return Base62内容
*/
public
byte
[]
encode
(
byte
[]
data
,
boolean
useInverted
)
{
final
Base62Encoder
encoder
=
useInverted
?
Base62Encoder
.
INVERTED_ENCODER
:
Base62Encoder
.
GMP_ENCODER
;
return
encoder
.
encode
(
data
);
}
/**
* 解码Base62消息
*
* @param encoded Base62内容
* @return 消息
*/
@Override
public
byte
[]
decode
(
byte
[]
encoded
)
{
return
decode
(
encoded
,
false
);
}
/**
* 解码Base62消息
*
* @param encoded Base62内容
* @param useInverted 是否使用反转风格,即将GMP风格中的大小写做转换
* @return 消息
*/
public
byte
[]
decode
(
byte
[]
encoded
,
boolean
useInverted
)
{
final
Base62Decoder
decoder
=
useInverted
?
Base62Decoder
.
INVERTED_DECODER
:
Base62Decoder
.
GMP_DECODER
;
return
decoder
.
decode
(
encoded
);
}
/**
* Base62编码器
*
* @since 5.8.0
*/
public
static
class
Base62Encoder
implements
Encoder
<
byte
[],
byte
[]>
{
/**
* GMP风格
*/
private
static
final
byte
[]
GMP
=
{
//
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
//
'8'
,
'9'
,
'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'
//
};
/**
* 反转风格,即将GMP风格中的大小写做转换
*/
private
static
final
byte
[]
INVERTED
=
{
//
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
//
'8'
,
'9'
,
'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'
//
};
public
static
Base62Encoder
GMP_ENCODER
=
new
Base62Encoder
(
GMP
);
public
static
Base62Encoder
INVERTED_ENCODER
=
new
Base62Encoder
(
INVERTED
);
private
final
byte
[]
alphabet
;
/**
* 构造
*
* @param alphabet 字符表
*/
public
Base62Encoder
(
byte
[]
alphabet
)
{
this
.
alphabet
=
alphabet
;
}
@Override
public
byte
[]
encode
(
byte
[]
data
)
{
final
byte
[]
indices
=
convert
(
data
,
STANDARD_BASE
,
TARGET_BASE
);
return
translate
(
indices
,
alphabet
);
}
}
/**
* Base62解码器
*
* @since 5.8.0
*/
public
static
class
Base62Decoder
implements
Decoder
<
byte
[],
byte
[]>
{
public
static
Base62Decoder
GMP_DECODER
=
new
Base62Decoder
(
Base62Encoder
.
GMP
);
public
static
Base62Decoder
INVERTED_DECODER
=
new
Base62Decoder
(
Base62Encoder
.
INVERTED
);
private
final
byte
[]
lookupTable
;
/**
* 构造
*
* @param alphabet 字母表
*/
public
Base62Decoder
(
byte
[]
alphabet
)
{
lookupTable
=
new
byte
[
'z'
+
1
];
for
(
int
i
=
0
;
i
<
alphabet
.
length
;
i
++)
{
lookupTable
[
alphabet
[
i
]]
=
(
byte
)
i
;
}
}
@Override
public
byte
[]
decode
(
byte
[]
encoded
)
{
final
byte
[]
prepared
=
translate
(
encoded
,
lookupTable
);
return
convert
(
prepared
,
TARGET_BASE
,
STANDARD_BASE
);
}
}
// region Private Methods
/**
* 按照字典转换bytes
*
* @param indices 内容
* @param dictionary 字典
* @return 转换值
*/
private
static
byte
[]
translate
(
byte
[]
indices
,
byte
[]
dictionary
)
{
final
byte
[]
translation
=
new
byte
[
indices
.
length
];
for
(
int
i
=
0
;
i
<
indices
.
length
;
i
++)
{
translation
[
i
]
=
dictionary
[
indices
[
i
]];
}
return
translation
;
}
/**
* 使用定义的字母表从源基准到目标基准
*
* @param message 消息bytes
* @param sourceBase 源基准长度
* @param targetBase 目标基准长度
* @return 计算结果
*/
private
static
byte
[]
convert
(
byte
[]
message
,
int
sourceBase
,
int
targetBase
)
{
// 计算结果长度,算法来自:http://codegolf.stackexchange.com/a/21672
final
int
estimatedLength
=
estimateOutputLength
(
message
.
length
,
sourceBase
,
targetBase
);
final
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
(
estimatedLength
);
byte
[]
source
=
message
;
while
(
source
.
length
>
0
)
{
final
ByteArrayOutputStream
quotient
=
new
ByteArrayOutputStream
(
source
.
length
);
int
remainder
=
0
;
for
(
byte
b
:
source
)
{
final
int
accumulator
=
(
b
&
0xFF
)
+
remainder
*
sourceBase
;
final
int
digit
=
(
accumulator
-
(
accumulator
%
targetBase
))
/
targetBase
;
remainder
=
accumulator
%
targetBase
;
if
(
quotient
.
size
()
>
0
||
digit
>
0
)
{
quotient
.
write
(
digit
);
}
}
out
.
write
(
remainder
);
source
=
quotient
.
toByteArray
();
}
// pad output with zeroes corresponding to the number of leading zeroes in the message
for
(
int
i
=
0
;
i
<
message
.
length
-
1
&&
message
[
i
]
==
0
;
i
++)
{
out
.
write
(
0
);
}
return
ArrayUtil
.
reverse
(
out
.
toByteArray
());
}
/**
* 估算结果长度
*
* @param inputLength 输入长度
* @param sourceBase 源基准长度
* @param targetBase 目标基准长度
* @return 估算长度
*/
private
static
int
estimateOutputLength
(
int
inputLength
,
int
sourceBase
,
int
targetBase
)
{
return
(
int
)
Math
.
ceil
((
Math
.
log
(
sourceBase
)
/
Math
.
log
(
targetBase
))
*
inputLength
);
}
// endregion
}
hutool-core/src/main/java/cn/hutool/core/codec/Base64.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.io.IoUtil
;
import
cn.hutool.core.util.CharsetUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.io.File
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.nio.charset.Charset
;
/**
* Base64工具类,提供Base64的编码和解码方案<br>
* base64编码是用64(2的6次方)个ASCII字符来表示256(2的8次方)个ASCII字符,<br>
* 也就是三位二进制数组经过编码后变为四位的ASCII字符显示,长度比原来增加1/3。
*
* @author Looly
*/
public
class
Base64
{
private
static
final
Charset
DEFAULT_CHARSET
=
CharsetUtil
.
CHARSET_UTF_8
;
// -------------------------------------------------------------------- encode
/**
* 编码为Base64,非URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
*/
public
static
byte
[]
encode
(
byte
[]
arr
,
boolean
lineSep
)
{
return
lineSep
?
java
.
util
.
Base64
.
getMimeEncoder
().
encode
(
arr
)
:
java
.
util
.
Base64
.
getEncoder
().
encode
(
arr
);
}
/**
* 编码为Base64,URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
* @since 3.0.6
* @deprecated 按照RFC2045规范,URL安全的Base64无需换行
*/
@Deprecated
public
static
byte
[]
encodeUrlSafe
(
byte
[]
arr
,
boolean
lineSep
)
{
return
Base64Encoder
.
encodeUrlSafe
(
arr
,
lineSep
);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
CharSequence
source
)
{
return
encode
(
source
,
DEFAULT_CHARSET
);
}
/**
* base64编码,URL安全
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public
static
String
encodeUrlSafe
(
CharSequence
source
)
{
return
encodeUrlSafe
(
source
,
DEFAULT_CHARSET
);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
encode
(
CharSequence
source
,
String
charset
)
{
return
encode
(
source
,
CharsetUtil
.
charset
(
charset
));
}
/**
* base64编码,不进行padding(末尾不会填充'=')
*
* @param source 被编码的base64字符串
* @param charset 编码
* @return 被加密后的字符串
* @since 5.5.2
*/
public
static
String
encodeWithoutPadding
(
CharSequence
source
,
String
charset
)
{
return
encodeWithoutPadding
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* base64编码,URL安全
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
* @deprecated 请使用 {@link #encodeUrlSafe(CharSequence, Charset)}
*/
@Deprecated
public
static
String
encodeUrlSafe
(
CharSequence
source
,
String
charset
)
{
return
encodeUrlSafe
(
source
,
CharsetUtil
.
charset
(
charset
));
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被编码后的字符串
*/
public
static
String
encode
(
CharSequence
source
,
Charset
charset
)
{
return
encode
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
*/
public
static
String
encodeUrlSafe
(
CharSequence
source
,
Charset
charset
)
{
return
encodeUrlSafe
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
byte
[]
source
)
{
return
java
.
util
.
Base64
.
getEncoder
().
encodeToString
(
source
);
}
/**
* base64编码,不进行padding(末尾不会填充'=')
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 5.5.2
*/
public
static
String
encodeWithoutPadding
(
byte
[]
source
)
{
return
java
.
util
.
Base64
.
getEncoder
().
withoutPadding
().
encodeToString
(
source
);
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public
static
String
encodeUrlSafe
(
byte
[]
source
)
{
return
java
.
util
.
Base64
.
getUrlEncoder
().
withoutPadding
().
encodeToString
(
source
);
}
/**
* base64编码
*
* @param in 被编码base64的流(一般为图片流或者文件流)
* @return 被加密后的字符串
* @since 4.0.9
*/
public
static
String
encode
(
InputStream
in
)
{
return
encode
(
IoUtil
.
readBytes
(
in
));
}
/**
* base64编码,URL安全的
*
* @param in 被编码base64的流(一般为图片流或者文件流)
* @return 被加密后的字符串
* @since 4.0.9
*/
public
static
String
encodeUrlSafe
(
InputStream
in
)
{
return
encodeUrlSafe
(
IoUtil
.
readBytes
(
in
));
}
/**
* base64编码
*
* @param file 被编码base64的文件
* @return 被加密后的字符串
* @since 4.0.9
*/
public
static
String
encode
(
File
file
)
{
return
encode
(
FileUtil
.
readBytes
(
file
));
}
/**
* base64编码,URL安全的
*
* @param file 被编码base64的文件
* @return 被加密后的字符串
* @since 4.0.9
*/
public
static
String
encodeUrlSafe
(
File
file
)
{
return
encodeUrlSafe
(
FileUtil
.
readBytes
(
file
));
}
/**
* 编码为Base64字符串<br>
* 如果isMultiLine为{@code true},则每76个字符一个换行符,否则在一行显示
*
* @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
* @param isUrlSafe 是否使用URL安全字符,一般为{@code false}
* @return 编码后的bytes
* @since 5.7.2
*/
public
static
String
encodeStr
(
byte
[]
arr
,
boolean
isMultiLine
,
boolean
isUrlSafe
)
{
return
StrUtil
.
str
(
encode
(
arr
,
isMultiLine
,
isUrlSafe
),
DEFAULT_CHARSET
);
}
/**
* 编码为Base64<br>
* 如果isMultiLine为{@code true},则每76个字符一个换行符,否则在一行显示
*
* @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
* @param isUrlSafe 是否使用URL安全字符,一般为{@code false}
* @return 编码后的bytes
*/
public
static
byte
[]
encode
(
byte
[]
arr
,
boolean
isMultiLine
,
boolean
isUrlSafe
)
{
return
Base64Encoder
.
encode
(
arr
,
isMultiLine
,
isUrlSafe
);
}
// -------------------------------------------------------------------- decode
/**
* base64解码
*
* @param source 被解码的base64字符串
* @return 被加密后的字符串
* @since 4.3.2
*/
public
static
String
decodeStrGbk
(
CharSequence
source
)
{
return
Base64Decoder
.
decodeStr
(
source
,
CharsetUtil
.
CHARSET_GBK
);
}
/**
* base64解码
*
* @param source 被解码的base64字符串
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
)
{
return
Base64Decoder
.
decodeStr
(
source
);
}
/**
* base64解码
*
* @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
,
String
charset
)
{
return
decodeStr
(
source
,
CharsetUtil
.
charset
(
charset
));
}
/**
* base64解码
*
* @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
,
Charset
charset
)
{
return
Base64Decoder
.
decodeStr
(
source
,
charset
);
}
/**
* base64解码
*
* @param base64 被解码的base64字符串
* @param destFile 目标文件
* @return 目标文件
* @since 4.0.9
*/
public
static
File
decodeToFile
(
CharSequence
base64
,
File
destFile
)
{
return
FileUtil
.
writeBytes
(
Base64Decoder
.
decode
(
base64
),
destFile
);
}
/**
* base64解码
*
* @param base64 被解码的base64字符串
* @param out 写出到的流
* @param isCloseOut 是否关闭输出流
* @since 4.0.9
*/
public
static
void
decodeToStream
(
CharSequence
base64
,
OutputStream
out
,
boolean
isCloseOut
)
{
IoUtil
.
write
(
out
,
isCloseOut
,
Base64Decoder
.
decode
(
base64
));
}
/**
* base64解码
*
* @param base64 被解码的base64字符串
* @return 解码后的bytes
*/
public
static
byte
[]
decode
(
CharSequence
base64
)
{
return
Base64Decoder
.
decode
(
base64
);
}
/**
* 解码Base64
*
* @param in 输入
* @return 解码后的bytes
*/
public
static
byte
[]
decode
(
byte
[]
in
)
{
return
Base64Decoder
.
decode
(
in
);
}
/**
* 检查是否为Base64
*
* @param base64 Base64的bytes
* @return 是否为Base64
* @since 5.7.5
*/
public
static
boolean
isBase64
(
CharSequence
base64
)
{
if
(
base64
==
null
||
base64
.
length
()
<
2
)
{
return
false
;
}
final
byte
[]
bytes
=
StrUtil
.
utf8Bytes
(
base64
);
if
(
bytes
.
length
!=
base64
.
length
())
{
// 如果长度不相等,说明存在双字节字符,肯定不是Base64,直接返回false
return
false
;
}
return
isBase64
(
bytes
);
}
/**
* 检查是否为Base64
*
* @param base64Bytes Base64的bytes
* @return 是否为Base64
* @since 5.7.5
*/
public
static
boolean
isBase64
(
byte
[]
base64Bytes
)
{
if
(
base64Bytes
==
null
||
base64Bytes
.
length
<
3
)
{
return
false
;
}
boolean
hasPadding
=
false
;
for
(
byte
base64Byte
:
base64Bytes
)
{
if
(
hasPadding
)
{
if
(
'='
!=
base64Byte
)
{
// 前一个字符是'=',则后边的字符都必须是'=',即'='只能都位于结尾
return
false
;
}
}
else
if
(
'='
==
base64Byte
)
{
// 发现'=' 标记之
hasPadding
=
true
;
}
else
if
(
false
==
(
Base64Decoder
.
isBase64Code
(
base64Byte
)
||
isWhiteSpace
(
base64Byte
)))
{
return
false
;
}
}
return
true
;
}
private
static
boolean
isWhiteSpace
(
byte
byteToCheck
)
{
switch
(
byteToCheck
)
{
case
' '
:
case
'\n'
:
case
'\r'
:
case
'\t'
:
return
true
;
default
:
return
false
;
}
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Base64Decoder.java
0 → 100644
View file @
45cda665
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
;
import
java.nio.charset.Charset
;
/**
* Base64解码实现
*
* @author looly
*
*/
public
class
Base64Decoder
{
private
static
final
Charset
DEFAULT_CHARSET
=
CharsetUtil
.
CHARSET_UTF_8
;
private
static
final
byte
PADDING
=
-
2
;
/** Base64解码表,共128位,-1表示非base64字符,-2表示padding */
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
,
-
2
,
-
1
,
-
1
,
// 30-3f 0-9,-2的位置是'='
-
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解码
*
* @param source 被解码的base64字符串
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
)
{
return
decodeStr
(
source
,
DEFAULT_CHARSET
);
}
/**
* base64解码
*
* @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
decodeStr
(
CharSequence
source
,
Charset
charset
)
{
return
StrUtil
.
str
(
decode
(
source
),
charset
);
}
/**
* base64解码
*
* @param source 被解码的base64字符串
* @return 被加密后的字符串
*/
public
static
byte
[]
decode
(
CharSequence
source
)
{
return
decode
(
StrUtil
.
bytes
(
source
,
DEFAULT_CHARSET
));
}
/**
* 解码Base64
*
* @param in 输入
* @return 解码后的bytes
*/
public
static
byte
[]
decode
(
byte
[]
in
)
{
if
(
ArrayUtil
.
isEmpty
(
in
))
{
return
in
;
}
return
decode
(
in
,
0
,
in
.
length
);
}
/**
* 解码Base64
*
* @param in 输入
* @param pos 开始位置
* @param length 长度
* @return 解码后的bytes
*/
public
static
byte
[]
decode
(
byte
[]
in
,
int
pos
,
int
length
)
{
if
(
ArrayUtil
.
isEmpty
(
in
))
{
return
in
;
}
final
MutableInt
offset
=
new
MutableInt
(
pos
);
byte
sestet0
;
byte
sestet1
;
byte
sestet2
;
byte
sestet3
;
int
maxPos
=
pos
+
length
-
1
;
int
octetId
=
0
;
byte
[]
octet
=
new
byte
[
length
*
3
/
4
];
// over-estimated if non-base64 characters present
while
(
offset
.
intValue
()
<=
maxPos
)
{
sestet0
=
getNextValidDecodeByte
(
in
,
offset
,
maxPos
);
sestet1
=
getNextValidDecodeByte
(
in
,
offset
,
maxPos
);
sestet2
=
getNextValidDecodeByte
(
in
,
offset
,
maxPos
);
sestet3
=
getNextValidDecodeByte
(
in
,
offset
,
maxPos
);
if
(
PADDING
!=
sestet1
)
{
octet
[
octetId
++]
=
(
byte
)
((
sestet0
<<
2
)
|
(
sestet1
>>>
4
));
}
if
(
PADDING
!=
sestet2
)
{
octet
[
octetId
++]
=
(
byte
)
(((
sestet1
&
0xf
)
<<
4
)
|
(
sestet2
>>>
2
));
}
if
(
PADDING
!=
sestet3
)
{
octet
[
octetId
++]
=
(
byte
)
(((
sestet2
&
3
)
<<
6
)
|
sestet3
);
}
}
if
(
octetId
==
octet
.
length
)
{
return
octet
;
}
else
{
// 如果有非Base64字符混入,则实际结果比解析的要短,截取之
return
(
byte
[])
ArrayUtil
.
copy
(
octet
,
new
byte
[
octetId
],
octetId
);
}
}
/**
* 给定的字符是否为Base64字符
*
* @param octet 被检查的字符
* @return 是否为Base64字符
* @since 5.7.5
*/
public
static
boolean
isBase64Code
(
byte
octet
)
{
return
octet
==
'='
||
(
octet
>=
0
&&
octet
<
DECODE_TABLE
.
length
&&
DECODE_TABLE
[
octet
]
!=
-
1
);
}
// ----------------------------------------------------------------------------------------------- Private start
/**
* 获取下一个有效的byte字符
*
* @param in 输入
* @param pos 当前位置,调用此方法后此位置保持在有效字符的下一个位置
* @param maxPos 最大位置
* @return 有效字符,如果达到末尾返回
*/
private
static
byte
getNextValidDecodeByte
(
byte
[]
in
,
MutableInt
pos
,
int
maxPos
)
{
byte
base64Byte
;
byte
decodeByte
;
while
(
pos
.
intValue
()
<=
maxPos
)
{
base64Byte
=
in
[
pos
.
intValue
()];
pos
.
increment
();
if
(
base64Byte
>
-
1
)
{
decodeByte
=
DECODE_TABLE
[
base64Byte
];
if
(
decodeByte
>
-
1
)
{
return
decodeByte
;
}
}
}
// padding if reached max position
return
PADDING
;
}
// ----------------------------------------------------------------------------------------------- Private end
}
hutool-core/src/main/java/cn/hutool/core/codec/Base64Encoder.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.util.CharsetUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.nio.charset.Charset
;
/**
* Base64编码<br>
* TODO 6.x移除此类,使用JDK自身
*
* @author looly
* @since 3.2.0
*/
public
class
Base64Encoder
{
private
static
final
Charset
DEFAULT_CHARSET
=
CharsetUtil
.
CHARSET_UTF_8
;
/**
* 标准编码表
*/
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'
,
'+'
,
'/'
//
};
/**
* URL安全的编码表,将 + 和 / 替换为 - 和 _
*/
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'
,
'-'
,
'_'
//
};
// -------------------------------------------------------------------- encode
/**
* 编码为Base64,非URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
*/
public
static
byte
[]
encode
(
byte
[]
arr
,
boolean
lineSep
)
{
return
encode
(
arr
,
lineSep
,
false
);
}
/**
* 编码为Base64,URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
* @since 3.0.6
*/
public
static
byte
[]
encodeUrlSafe
(
byte
[]
arr
,
boolean
lineSep
)
{
return
encode
(
arr
,
lineSep
,
true
);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
CharSequence
source
)
{
return
encode
(
source
,
DEFAULT_CHARSET
);
}
/**
* base64编码,URL安全
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public
static
String
encodeUrlSafe
(
CharSequence
source
)
{
return
encodeUrlSafe
(
source
,
DEFAULT_CHARSET
);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public
static
String
encode
(
CharSequence
source
,
Charset
charset
)
{
return
encode
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
*/
public
static
String
encodeUrlSafe
(
CharSequence
source
,
Charset
charset
)
{
return
encodeUrlSafe
(
StrUtil
.
bytes
(
source
,
charset
));
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public
static
String
encode
(
byte
[]
source
)
{
return
StrUtil
.
str
(
encode
(
source
,
false
),
DEFAULT_CHARSET
);
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public
static
String
encodeUrlSafe
(
byte
[]
source
)
{
return
StrUtil
.
str
(
encodeUrlSafe
(
source
,
false
),
DEFAULT_CHARSET
);
}
/**
* 编码为Base64字符串<br>
* 如果isMultiLine为{@code true},则每76个字符一个换行符,否则在一行显示
*
* @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
* @param isUrlSafe 是否使用URL安全字符,在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉,一般为{@code false}
* @return 编码后的bytes
* @since 5.7.2
*/
public
static
String
encodeStr
(
byte
[]
arr
,
boolean
isMultiLine
,
boolean
isUrlSafe
)
{
return
StrUtil
.
str
(
encode
(
arr
,
isMultiLine
,
isUrlSafe
),
DEFAULT_CHARSET
);
}
/**
* 编码为Base64<br>
* 如果isMultiLine为{@code true},则每76个字符一个换行符,否则在一行显示
*
* @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
* @param isUrlSafe 是否使用URL安全字符,在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉,一般为{@code false}
* @return 编码后的bytes
*/
public
static
byte
[]
encode
(
byte
[]
arr
,
boolean
isMultiLine
,
boolean
isUrlSafe
)
{
if
(
null
==
arr
)
{
return
null
;
}
int
len
=
arr
.
length
;
if
(
len
==
0
)
{
return
new
byte
[
0
];
}
int
evenlen
=
(
len
/
3
)
*
3
;
int
cnt
=
((
len
-
1
)
/
3
+
1
)
<<
2
;
int
destlen
=
cnt
+
(
isMultiLine
?
(
cnt
-
1
)
/
76
<<
1
:
0
);
byte
[]
dest
=
new
byte
[
destlen
];
byte
[]
encodeTable
=
isUrlSafe
?
URL_SAFE_ENCODE_TABLE
:
STANDARD_ENCODE_TABLE
;
for
(
int
s
=
0
,
d
=
0
,
cc
=
0
;
s
<
evenlen
;
)
{
int
i
=
(
arr
[
s
++]
&
0xff
)
<<
16
|
(
arr
[
s
++]
&
0xff
)
<<
8
|
(
arr
[
s
++]
&
0xff
);
dest
[
d
++]
=
encodeTable
[(
i
>>>
18
)
&
0x3f
];
dest
[
d
++]
=
encodeTable
[(
i
>>>
12
)
&
0x3f
];
dest
[
d
++]
=
encodeTable
[(
i
>>>
6
)
&
0x3f
];
dest
[
d
++]
=
encodeTable
[
i
&
0x3f
];
if
(
isMultiLine
&&
++
cc
==
19
&&
d
<
destlen
-
2
)
{
dest
[
d
++]
=
'\r'
;
dest
[
d
++]
=
'\n'
;
cc
=
0
;
}
}
int
left
=
len
-
evenlen
;
// 剩余位数
if
(
left
>
0
)
{
int
i
=
((
arr
[
evenlen
]
&
0xff
)
<<
10
)
|
(
left
==
2
?
((
arr
[
len
-
1
]
&
0xff
)
<<
2
)
:
0
);
dest
[
destlen
-
4
]
=
encodeTable
[
i
>>
12
];
dest
[
destlen
-
3
]
=
encodeTable
[(
i
>>>
6
)
&
0x3f
];
if
(
isUrlSafe
)
{
// 在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉。
int
urlSafeLen
=
destlen
-
2
;
if
(
2
==
left
)
{
dest
[
destlen
-
2
]
=
encodeTable
[
i
&
0x3f
];
urlSafeLen
+=
1
;
}
byte
[]
urlSafeDest
=
new
byte
[
urlSafeLen
];
System
.
arraycopy
(
dest
,
0
,
urlSafeDest
,
0
,
urlSafeLen
);
return
urlSafeDest
;
}
else
{
dest
[
destlen
-
2
]
=
(
left
==
2
)
?
encodeTable
[
i
&
0x3f
]
:
(
byte
)
'='
;
dest
[
destlen
-
1
]
=
'='
;
}
}
return
dest
;
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Caesar.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
cn.hutool.core.lang.Assert
;
/**
* 凯撒密码实现<br>
* 算法来自:https://github.com/zhaorenjie110/SymmetricEncryptionAndDecryption
*
* @author looly
*/
public
class
Caesar
{
// 26个字母表
public
static
final
String
TABLE
=
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
;
/**
* 传入明文,加密得到密文
*
* @param message 加密的消息
* @param offset 偏移量
* @return 加密后的内容
*/
public
static
String
encode
(
String
message
,
int
offset
)
{
Assert
.
notNull
(
message
,
"message must be not null!"
);
final
int
len
=
message
.
length
();
final
char
[]
plain
=
message
.
toCharArray
();
char
c
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
c
=
message
.
charAt
(
i
);
if
(
false
==
Character
.
isLetter
(
c
))
{
continue
;
}
plain
[
i
]
=
encodeChar
(
c
,
offset
);
}
return
new
String
(
plain
);
}
/**
* 传入明文解密到密文
*
* @param cipherText 密文
* @param offset 偏移量
* @return 解密后的内容
*/
public
static
String
decode
(
String
cipherText
,
int
offset
)
{
Assert
.
notNull
(
cipherText
,
"cipherText must be not null!"
);
final
int
len
=
cipherText
.
length
();
final
char
[]
plain
=
cipherText
.
toCharArray
();
char
c
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
c
=
cipherText
.
charAt
(
i
);
if
(
false
==
Character
.
isLetter
(
c
))
{
continue
;
}
plain
[
i
]
=
decodeChar
(
c
,
offset
);
}
return
new
String
(
plain
);
}
// ----------------------------------------------------------------------------------------- Private method start
/**
* 加密轮盘
*
* @param c 被加密字符
* @param offset 偏移量
* @return 加密后的字符
*/
private
static
char
encodeChar
(
char
c
,
int
offset
)
{
int
position
=
(
TABLE
.
indexOf
(
c
)
+
offset
)
%
52
;
return
TABLE
.
charAt
(
position
);
}
/**
* 解密轮盘
*
* @param c 字符
* @return 解密后的字符
*/
private
static
char
decodeChar
(
char
c
,
int
offset
)
{
int
position
=
(
TABLE
.
indexOf
(
c
)
-
offset
)
%
52
;
if
(
position
<
0
)
{
position
+=
52
;
}
return
TABLE
.
charAt
(
position
);
}
// ----------------------------------------------------------------------------------------- Private method end
}
hutool-core/src/main/java/cn/hutool/core/codec/Decoder.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
/**
* 解码接口
*
* @param <T> 被解码的数据类型
* @param <R> 解码后的数据类型
* @author looly
* @since 5.7.22
*/
public
interface
Decoder
<
T
,
R
>
{
/**
* 执行解码
*
* @param encoded 被解码的数据
* @return 解码后的数据
*/
R
decode
(
T
encoded
);
}
hutool-core/src/main/java/cn/hutool/core/codec/Encoder.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
/**
* 编码接口
*
* @param <T> 被编码的数据类型
* @param <R> 编码后的数据类型
* @author looly
* @since 5.7.22
*/
public
interface
Encoder
<
T
,
R
>
{
/**
* 执行编码
*
* @param data 被编码的数据
* @return 编码后的数据
*/
R
encode
(
T
data
);
}
hutool-core/src/main/java/cn/hutool/core/codec/Hashids.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
java.math.BigInteger
;
import
java.util.Arrays
;
import
java.util.LinkedHashSet
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Set
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.stream.Collectors
;
import
java.util.stream.IntStream
;
import
java.util.stream.LongStream
;
/**
* <a href="http://hashids.org/">Hashids</a> 协议实现,以实现:
* <ul>
* <li>生成简短、唯一、大小写敏感并无序的hash值</li>
* <li>自然数字的Hash值</li>
* <li>可以设置不同的盐,具有保密性</li>
* <li>可配置的hash长度</li>
* <li>递增的输入产生的输出无法预测</li>
* </ul>
*
* <p>
* 来自:<a href="https://github.com/davidafsilva/java-hashids">https://github.com/davidafsilva/java-hashids</a>
* </p>
*
* <p>
* {@code Hashids}可以将数字或者16进制字符串转为短且唯一不连续的字符串,采用双向编码实现,比如,它可以将347之类的数字转换为yr8之类的字符串,也可以将yr8之类的字符串重新解码为347之类的数字。<br>
* 此编码算法主要是解决爬虫类应用对连续ID爬取问题,将有序的ID转换为无序的Hashids,而且一一对应。
* </p>
*
* @author david
*/
public
class
Hashids
implements
Encoder
<
long
[],
String
>,
Decoder
<
String
,
long
[]>
{
private
static
final
int
LOTTERY_MOD
=
100
;
private
static
final
double
GUARD_THRESHOLD
=
12
;
private
static
final
double
SEPARATOR_THRESHOLD
=
3.5
;
// 最小编解码字符串
private
static
final
int
MIN_ALPHABET_LENGTH
=
16
;
private
static
final
Pattern
HEX_VALUES_PATTERN
=
Pattern
.
compile
(
"[\\w\\W]{1,12}"
);
// 默认编解码字符串
public
static
final
char
[]
DEFAULT_ALPHABET
=
{
'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'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'0'
};
// 默认分隔符
private
static
final
char
[]
DEFAULT_SEPARATORS
=
{
'c'
,
'f'
,
'h'
,
'i'
,
's'
,
't'
,
'u'
,
'C'
,
'F'
,
'H'
,
'I'
,
'S'
,
'T'
,
'U'
};
// algorithm properties
private
final
char
[]
alphabet
;
// 多个数字编解码的分界符
private
final
char
[]
separators
;
private
final
Set
<
Character
>
separatorsSet
;
private
final
char
[]
salt
;
// 补齐至 minLength 长度添加的字符列表
private
final
char
[]
guards
;
// 编码后最小的字符长度
private
final
int
minLength
;
// region create
/**
* 根据参数值,创建{@code Hashids},使用默认{@link #DEFAULT_ALPHABET}作为字母表,不限制最小长度
*
* @param salt 加盐值
* @return {@code Hashids}
*/
public
static
Hashids
create
(
final
char
[]
salt
)
{
return
create
(
salt
,
DEFAULT_ALPHABET
,
-
1
);
}
/**
* 根据参数值,创建{@code Hashids},使用默认{@link #DEFAULT_ALPHABET}作为字母表
*
* @param salt 加盐值
* @param minLength 限制最小长度,-1表示不限制
* @return {@code Hashids}
*/
public
static
Hashids
create
(
final
char
[]
salt
,
final
int
minLength
)
{
return
create
(
salt
,
DEFAULT_ALPHABET
,
minLength
);
}
/**
* 根据参数值,创建{@code Hashids}
*
* @param salt 加盐值
* @param alphabet hash字母表
* @param minLength 限制最小长度,-1表示不限制
* @return {@code Hashids}
*/
public
static
Hashids
create
(
final
char
[]
salt
,
final
char
[]
alphabet
,
final
int
minLength
)
{
return
new
Hashids
(
salt
,
alphabet
,
minLength
);
}
// endregion
/**
* 构造
*
* @param salt 加盐值
* @param alphabet hash字母表
* @param minLength 限制最小长度,-1表示不限制
*/
public
Hashids
(
final
char
[]
salt
,
final
char
[]
alphabet
,
final
int
minLength
)
{
this
.
minLength
=
minLength
;
this
.
salt
=
Arrays
.
copyOf
(
salt
,
salt
.
length
);
// filter and shuffle separators
char
[]
tmpSeparators
=
shuffle
(
filterSeparators
(
DEFAULT_SEPARATORS
,
alphabet
),
this
.
salt
);
// validate and filter the alphabet
char
[]
tmpAlphabet
=
validateAndFilterAlphabet
(
alphabet
,
tmpSeparators
);
// check separator threshold
if
(
tmpSeparators
.
length
==
0
||
((
double
)
(
tmpAlphabet
.
length
/
tmpSeparators
.
length
))
>
SEPARATOR_THRESHOLD
)
{
final
int
minSeparatorsSize
=
(
int
)
Math
.
ceil
(
tmpAlphabet
.
length
/
SEPARATOR_THRESHOLD
);
// check minimum size of separators
if
(
minSeparatorsSize
>
tmpSeparators
.
length
)
{
// fill separators from alphabet
final
int
missingSeparators
=
minSeparatorsSize
-
tmpSeparators
.
length
;
tmpSeparators
=
Arrays
.
copyOf
(
tmpSeparators
,
tmpSeparators
.
length
+
missingSeparators
);
System
.
arraycopy
(
tmpAlphabet
,
0
,
tmpSeparators
,
tmpSeparators
.
length
-
missingSeparators
,
missingSeparators
);
System
.
arraycopy
(
tmpAlphabet
,
0
,
tmpSeparators
,
tmpSeparators
.
length
-
missingSeparators
,
missingSeparators
);
tmpAlphabet
=
Arrays
.
copyOfRange
(
tmpAlphabet
,
missingSeparators
,
tmpAlphabet
.
length
);
}
}
// shuffle the current alphabet
shuffle
(
tmpAlphabet
,
this
.
salt
);
// check guards
this
.
guards
=
new
char
[(
int
)
Math
.
ceil
(
tmpAlphabet
.
length
/
GUARD_THRESHOLD
)];
if
(
alphabet
.
length
<
3
)
{
System
.
arraycopy
(
tmpSeparators
,
0
,
guards
,
0
,
guards
.
length
);
this
.
separators
=
Arrays
.
copyOfRange
(
tmpSeparators
,
guards
.
length
,
tmpSeparators
.
length
);
this
.
alphabet
=
tmpAlphabet
;
}
else
{
System
.
arraycopy
(
tmpAlphabet
,
0
,
guards
,
0
,
guards
.
length
);
this
.
separators
=
tmpSeparators
;
this
.
alphabet
=
Arrays
.
copyOfRange
(
tmpAlphabet
,
guards
.
length
,
tmpAlphabet
.
length
);
}
// create the separators set
separatorsSet
=
IntStream
.
range
(
0
,
separators
.
length
)
.
mapToObj
(
idx
->
separators
[
idx
])
.
collect
(
Collectors
.
toSet
());
}
/**
* 编码给定的16进制数字
*
* @param hexNumbers 16进制数字
* @return 编码后的值, {@code null} if {@code numbers} 是 {@code null}.
* @throws IllegalArgumentException 数字不支持抛出此异常
*/
public
String
encodeFromHex
(
final
String
hexNumbers
)
{
if
(
hexNumbers
==
null
)
{
return
null
;
}
// remove the prefix, if present
final
String
hex
=
hexNumbers
.
startsWith
(
"0x"
)
||
hexNumbers
.
startsWith
(
"0X"
)
?
hexNumbers
.
substring
(
2
)
:
hexNumbers
;
// get the associated long value and encode it
LongStream
values
=
LongStream
.
empty
();
final
Matcher
matcher
=
HEX_VALUES_PATTERN
.
matcher
(
hex
);
while
(
matcher
.
find
())
{
final
long
value
=
new
BigInteger
(
"1"
+
matcher
.
group
(),
16
).
longValue
();
values
=
LongStream
.
concat
(
values
,
LongStream
.
of
(
value
));
}
return
encode
(
values
.
toArray
());
}
/**
* 编码给定的数字数组
*
* @param numbers 数字数组
* @return 编码后的值, {@code null} if {@code numbers} 是 {@code null}.
* @throws IllegalArgumentException 数字不支持抛出此异常
*/
@Override
public
String
encode
(
final
long
...
numbers
)
{
if
(
numbers
==
null
)
{
return
null
;
}
// copy alphabet
final
char
[]
currentAlphabet
=
Arrays
.
copyOf
(
alphabet
,
alphabet
.
length
);
// determine the lottery number
final
long
lotteryId
=
LongStream
.
range
(
0
,
numbers
.
length
)
.
reduce
(
0
,
(
state
,
i
)
->
{
final
long
number
=
numbers
[(
int
)
i
];
if
(
number
<
0
)
{
throw
new
IllegalArgumentException
(
"invalid number: "
+
number
);
}
return
state
+
number
%
(
i
+
LOTTERY_MOD
);
});
final
char
lottery
=
currentAlphabet
[(
int
)
(
lotteryId
%
currentAlphabet
.
length
)];
// encode each number
final
StringBuilder
global
=
new
StringBuilder
();
IntStream
.
range
(
0
,
numbers
.
length
)
.
forEach
(
idx
->
{
// derive alphabet
deriveNewAlphabet
(
currentAlphabet
,
salt
,
lottery
);
// encode
final
int
initialLength
=
global
.
length
();
translate
(
numbers
[
idx
],
currentAlphabet
,
global
,
initialLength
);
// prepend the lottery
if
(
idx
==
0
)
{
global
.
insert
(
0
,
lottery
);
}
// append the separator, if more numbers are pending encoding
if
(
idx
+
1
<
numbers
.
length
)
{
long
n
=
numbers
[
idx
]
%
(
global
.
charAt
(
initialLength
)
+
1
);
global
.
append
(
separators
[(
int
)
(
n
%
separators
.
length
)]);
}
});
// add the guards, if there's any space left
if
(
minLength
>
global
.
length
())
{
int
guardIdx
=
(
int
)
((
lotteryId
+
lottery
)
%
guards
.
length
);
global
.
insert
(
0
,
guards
[
guardIdx
]);
if
(
minLength
>
global
.
length
())
{
guardIdx
=
(
int
)
((
lotteryId
+
global
.
charAt
(
2
))
%
guards
.
length
);
global
.
append
(
guards
[
guardIdx
]);
}
}
// add the necessary padding
int
paddingLeft
=
minLength
-
global
.
length
();
while
(
paddingLeft
>
0
)
{
shuffle
(
currentAlphabet
,
Arrays
.
copyOf
(
currentAlphabet
,
currentAlphabet
.
length
));
final
int
alphabetHalfSize
=
currentAlphabet
.
length
/
2
;
final
int
initialSize
=
global
.
length
();
if
(
paddingLeft
>
currentAlphabet
.
length
)
{
// entire alphabet with the current encoding in the middle of it
int
offset
=
alphabetHalfSize
+
(
currentAlphabet
.
length
%
2
==
0
?
0
:
1
);
global
.
insert
(
0
,
currentAlphabet
,
alphabetHalfSize
,
offset
);
global
.
insert
(
offset
+
initialSize
,
currentAlphabet
,
0
,
alphabetHalfSize
);
// decrease the padding left
paddingLeft
-=
currentAlphabet
.
length
;
}
else
{
// calculate the excess
final
int
excess
=
currentAlphabet
.
length
+
global
.
length
()
-
minLength
;
final
int
secondHalfStartOffset
=
alphabetHalfSize
+
Math
.
floorDiv
(
excess
,
2
);
final
int
secondHalfLength
=
currentAlphabet
.
length
-
secondHalfStartOffset
;
final
int
firstHalfLength
=
paddingLeft
-
secondHalfLength
;
global
.
insert
(
0
,
currentAlphabet
,
secondHalfStartOffset
,
secondHalfLength
);
global
.
insert
(
secondHalfLength
+
initialSize
,
currentAlphabet
,
0
,
firstHalfLength
);
paddingLeft
=
0
;
}
}
return
global
.
toString
();
}
//-------------------------
// Decode
//-------------------------
/**
* 解码Hash值为16进制数字
*
* @param hash hash值
* @return 解码后的16进制值, {@code null} if {@code numbers} 是 {@code null}.
* @throws IllegalArgumentException if the hash is invalid.
*/
public
String
decodeToHex
(
final
String
hash
)
{
if
(
hash
==
null
)
{
return
null
;
}
final
StringBuilder
sb
=
new
StringBuilder
();
Arrays
.
stream
(
decode
(
hash
))
.
mapToObj
(
Long:
:
toHexString
)
.
forEach
(
hex
->
sb
.
append
(
hex
,
1
,
hex
.
length
()));
return
sb
.
toString
();
}
/**
* 解码Hash值为数字数组
*
* @param hash hash值
* @return 解码后的16进制值, {@code null} if {@code numbers} 是 {@code null}.
* @throws IllegalArgumentException if the hash is invalid.
*/
@Override
public
long
[]
decode
(
final
String
hash
)
{
if
(
hash
==
null
)
{
return
null
;
}
// create a set of the guards
final
Set
<
Character
>
guardsSet
=
IntStream
.
range
(
0
,
guards
.
length
)
.
mapToObj
(
idx
->
guards
[
idx
])
.
collect
(
Collectors
.
toSet
());
// count the total guards used
final
int
[]
guardsIdx
=
IntStream
.
range
(
0
,
hash
.
length
())
.
filter
(
idx
->
guardsSet
.
contains
(
hash
.
charAt
(
idx
)))
.
toArray
();
// get the start/end index base on the guards count
final
int
startIdx
,
endIdx
;
if
(
guardsIdx
.
length
>
0
)
{
startIdx
=
guardsIdx
[
0
]
+
1
;
endIdx
=
guardsIdx
.
length
>
1
?
guardsIdx
[
1
]
:
hash
.
length
();
}
else
{
startIdx
=
0
;
endIdx
=
hash
.
length
();
}
LongStream
decoded
=
LongStream
.
empty
();
// parse the hash
if
(
hash
.
length
()
>
0
)
{
final
char
lottery
=
hash
.
charAt
(
startIdx
);
// create the initial accumulation string
final
int
length
=
hash
.
length
()
-
guardsIdx
.
length
-
1
;
StringBuilder
block
=
new
StringBuilder
(
length
);
// create the base salt
final
char
[]
decodeSalt
=
new
char
[
alphabet
.
length
];
decodeSalt
[
0
]
=
lottery
;
final
int
saltLength
=
salt
.
length
>=
alphabet
.
length
?
alphabet
.
length
-
1
:
salt
.
length
;
System
.
arraycopy
(
salt
,
0
,
decodeSalt
,
1
,
saltLength
);
final
int
saltLeft
=
alphabet
.
length
-
saltLength
-
1
;
// copy alphabet
final
char
[]
currentAlphabet
=
Arrays
.
copyOf
(
alphabet
,
alphabet
.
length
);
for
(
int
i
=
startIdx
+
1
;
i
<
endIdx
;
i
++)
{
if
(
false
==
separatorsSet
.
contains
(
hash
.
charAt
(
i
)))
{
block
.
append
(
hash
.
charAt
(
i
));
// continue if we have not reached the end, yet
if
(
i
<
endIdx
-
1
)
{
continue
;
}
}
if
(
block
.
length
()
>
0
)
{
// create the salt
if
(
saltLeft
>
0
)
{
System
.
arraycopy
(
currentAlphabet
,
0
,
decodeSalt
,
alphabet
.
length
-
saltLeft
,
saltLeft
);
}
// shuffle the alphabet
shuffle
(
currentAlphabet
,
decodeSalt
);
// prepend the decoded value
final
long
n
=
translate
(
block
.
toString
().
toCharArray
(),
currentAlphabet
);
decoded
=
LongStream
.
concat
(
decoded
,
LongStream
.
of
(
n
));
// create a new block
block
=
new
StringBuilder
(
length
);
}
}
}
// validate the hash
final
long
[]
decodedValue
=
decoded
.
toArray
();
if
(!
Objects
.
equals
(
hash
,
encode
(
decodedValue
)))
{
throw
new
IllegalArgumentException
(
"invalid hash: "
+
hash
);
}
return
decodedValue
;
}
private
StringBuilder
translate
(
final
long
n
,
final
char
[]
alphabet
,
final
StringBuilder
sb
,
final
int
start
)
{
long
input
=
n
;
do
{
// prepend the chosen char
sb
.
insert
(
start
,
alphabet
[(
int
)
(
input
%
alphabet
.
length
)]);
// trim the input
input
=
input
/
alphabet
.
length
;
}
while
(
input
>
0
);
return
sb
;
}
private
long
translate
(
final
char
[]
hash
,
final
char
[]
alphabet
)
{
long
number
=
0
;
final
Map
<
Character
,
Integer
>
alphabetMapping
=
IntStream
.
range
(
0
,
alphabet
.
length
)
.
mapToObj
(
idx
->
new
Object
[]{
alphabet
[
idx
],
idx
})
.
collect
(
Collectors
.
groupingBy
(
arr
->
(
Character
)
arr
[
0
],
Collectors
.
mapping
(
arr
->
(
Integer
)
arr
[
1
],
Collectors
.
reducing
(
null
,
(
a
,
b
)
->
a
==
null
?
b
:
a
))));
for
(
int
i
=
0
;
i
<
hash
.
length
;
++
i
)
{
number
+=
alphabetMapping
.
computeIfAbsent
(
hash
[
i
],
k
->
{
throw
new
IllegalArgumentException
(
"Invalid alphabet for hash"
);
})
*
(
long
)
Math
.
pow
(
alphabet
.
length
,
hash
.
length
-
i
-
1
);
}
return
number
;
}
private
char
[]
deriveNewAlphabet
(
final
char
[]
alphabet
,
final
char
[]
salt
,
final
char
lottery
)
{
// create the new salt
final
char
[]
newSalt
=
new
char
[
alphabet
.
length
];
// 1. lottery
newSalt
[
0
]
=
lottery
;
int
spaceLeft
=
newSalt
.
length
-
1
;
int
offset
=
1
;
// 2. salt
if
(
salt
.
length
>
0
&&
spaceLeft
>
0
)
{
int
length
=
Math
.
min
(
salt
.
length
,
spaceLeft
);
System
.
arraycopy
(
salt
,
0
,
newSalt
,
offset
,
length
);
spaceLeft
-=
length
;
offset
+=
length
;
}
// 3. alphabet
if
(
spaceLeft
>
0
)
{
System
.
arraycopy
(
alphabet
,
0
,
newSalt
,
offset
,
spaceLeft
);
}
// shuffle
return
shuffle
(
alphabet
,
newSalt
);
}
private
char
[]
validateAndFilterAlphabet
(
final
char
[]
alphabet
,
final
char
[]
separators
)
{
// validate size
if
(
alphabet
.
length
<
MIN_ALPHABET_LENGTH
)
{
throw
new
IllegalArgumentException
(
String
.
format
(
"alphabet must contain at least %d unique "
+
"characters: %d"
,
MIN_ALPHABET_LENGTH
,
alphabet
.
length
));
}
final
Set
<
Character
>
seen
=
new
LinkedHashSet
<>(
alphabet
.
length
);
final
Set
<
Character
>
invalid
=
IntStream
.
range
(
0
,
separators
.
length
)
.
mapToObj
(
idx
->
separators
[
idx
])
.
collect
(
Collectors
.
toSet
());
// add to seen set (without duplicates)
IntStream
.
range
(
0
,
alphabet
.
length
)
.
forEach
(
i
->
{
if
(
alphabet
[
i
]
==
' '
)
{
throw
new
IllegalArgumentException
(
String
.
format
(
"alphabet must not contain spaces: "
+
"index %d"
,
i
));
}
final
Character
c
=
alphabet
[
i
];
if
(!
invalid
.
contains
(
c
))
{
seen
.
add
(
c
);
}
});
// create a new alphabet without the duplicates
final
char
[]
uniqueAlphabet
=
new
char
[
seen
.
size
()];
int
idx
=
0
;
for
(
char
c
:
seen
)
{
uniqueAlphabet
[
idx
++]
=
c
;
}
return
uniqueAlphabet
;
}
@SuppressWarnings
(
"SameParameterValue"
)
private
char
[]
filterSeparators
(
final
char
[]
separators
,
final
char
[]
alphabet
)
{
final
Set
<
Character
>
valid
=
IntStream
.
range
(
0
,
alphabet
.
length
)
.
mapToObj
(
idx
->
alphabet
[
idx
])
.
collect
(
Collectors
.
toSet
());
return
IntStream
.
range
(
0
,
separators
.
length
)
.
mapToObj
(
idx
->
(
separators
[
idx
]))
.
filter
(
valid:
:
contains
)
// ugly way to convert back to char[]
.
map
(
c
->
Character
.
toString
(
c
))
.
collect
(
Collectors
.
joining
())
.
toCharArray
();
}
private
char
[]
shuffle
(
final
char
[]
alphabet
,
final
char
[]
salt
)
{
for
(
int
i
=
alphabet
.
length
-
1
,
v
=
0
,
p
=
0
,
j
,
z
;
salt
.
length
>
0
&&
i
>
0
;
i
--,
v
++)
{
v
%=
salt
.
length
;
p
+=
z
=
salt
[
v
];
j
=
(
z
+
v
+
p
)
%
i
;
final
char
tmp
=
alphabet
[
j
];
alphabet
[
j
]
=
alphabet
[
i
];
alphabet
[
i
]
=
tmp
;
}
return
alphabet
;
}
}
hutool-core/src/main/java/cn/hutool/core/codec/Morse.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.codec
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
cn.hutool.core.lang.Assert
;
import
cn.hutool.core.util.CharUtil
;
import
cn.hutool.core.util.StrUtil
;
/**
* 莫尔斯电码的编码和解码实现<br>
* 参考:https://github.com/TakWolf/Java-MorseCoder
*
* @author looly, TakWolf
* @since 4.4.1
*/
public
class
Morse
{
private
static
final
Map
<
Integer
,
String
>
ALPHABETS
=
new
HashMap
<>();
// code point -> morse
private
static
final
Map
<
String
,
Integer
>
DICTIONARIES
=
new
HashMap
<>();
// morse -> code point
/**
* 注册莫尔斯电码表
*
* @param abc 字母和字符
* @param dict 二进制
*/
private
static
void
registerMorse
(
Character
abc
,
String
dict
)
{
ALPHABETS
.
put
((
int
)
abc
,
dict
);
DICTIONARIES
.
put
(
dict
,
(
int
)
abc
);
}
static
{
// Letters
registerMorse
(
'A'
,
"01"
);
registerMorse
(
'B'
,
"1000"
);
registerMorse
(
'C'
,
"1010"
);
registerMorse
(
'D'
,
"100"
);
registerMorse
(
'E'
,
"0"
);
registerMorse
(
'F'
,
"0010"
);
registerMorse
(
'G'
,
"110"
);
registerMorse
(
'H'
,
"0000"
);
registerMorse
(
'I'
,
"00"
);
registerMorse
(
'J'
,
"0111"
);
registerMorse
(
'K'
,
"101"
);
registerMorse
(
'L'
,
"0100"
);
registerMorse
(
'M'
,
"11"
);
registerMorse
(
'N'
,
"10"
);
registerMorse
(
'O'
,
"111"
);
registerMorse
(
'P'
,
"0110"
);
registerMorse
(
'Q'
,
"1101"
);
registerMorse
(
'R'
,
"010"
);
registerMorse
(
'S'
,
"000"
);
registerMorse
(
'T'
,
"1"
);
registerMorse
(
'U'
,
"001"
);
registerMorse
(
'V'
,
"0001"
);
registerMorse
(
'W'
,
"011"
);
registerMorse
(
'X'
,
"1001"
);
registerMorse
(
'Y'
,
"1011"
);
registerMorse
(
'Z'
,
"1100"
);
// Numbers
registerMorse
(
'0'
,
"11111"
);
registerMorse
(
'1'
,
"01111"
);
registerMorse
(
'2'
,
"00111"
);
registerMorse
(
'3'
,
"00011"
);
registerMorse
(
'4'
,
"00001"
);
registerMorse
(
'5'
,
"00000"
);
registerMorse
(
'6'
,
"10000"
);
registerMorse
(
'7'
,
"11000"
);
registerMorse
(
'8'
,
"11100"
);
registerMorse
(
'9'
,
"11110"
);
// Punctuation
registerMorse
(
'.'
,
"010101"
);
registerMorse
(
','
,
"110011"
);
registerMorse
(
'?'
,
"001100"
);
registerMorse
(
'\''
,
"011110"
);
registerMorse
(
'!'
,
"101011"
);
registerMorse
(
'/'
,
"10010"
);
registerMorse
(
'('
,
"10110"
);
registerMorse
(
')'
,
"101101"
);
registerMorse
(
'&'
,
"01000"
);
registerMorse
(
':'
,
"111000"
);
registerMorse
(
';'
,
"101010"
);
registerMorse
(
'='
,
"10001"
);
registerMorse
(
'+'
,
"01010"
);
registerMorse
(
'-'
,
"100001"
);
registerMorse
(
'_'
,
"001101"
);
registerMorse
(
'"'
,
"010010"
);
registerMorse
(
'$'
,
"0001001"
);
registerMorse
(
'@'
,
"011010"
);
}
private
final
char
dit
;
// short mark or dot
private
final
char
dah
;
// longer mark or dash
private
final
char
split
;
/**
* 构造
*/
public
Morse
()
{
this
(
CharUtil
.
DOT
,
CharUtil
.
DASHED
,
CharUtil
.
SLASH
);
}
/**
* 构造
*
* @param dit 点表示的字符
* @param dah 横线表示的字符
* @param split 分隔符
*/
public
Morse
(
char
dit
,
char
dah
,
char
split
)
{
this
.
dit
=
dit
;
this
.
dah
=
dah
;
this
.
split
=
split
;
}
/**
* 编码
*
* @param text 文本
* @return 密文
*/
public
String
encode
(
String
text
)
{
Assert
.
notNull
(
text
,
"Text should not be null."
);
text
=
text
.
toUpperCase
();
final
StringBuilder
morseBuilder
=
new
StringBuilder
();
final
int
len
=
text
.
codePointCount
(
0
,
text
.
length
());
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
int
codePoint
=
text
.
codePointAt
(
i
);
String
word
=
ALPHABETS
.
get
(
codePoint
);
if
(
word
==
null
)
{
word
=
Integer
.
toBinaryString
(
codePoint
);
}
morseBuilder
.
append
(
word
.
replace
(
'0'
,
dit
).
replace
(
'1'
,
dah
)).
append
(
split
);
}
return
morseBuilder
.
toString
();
}
/**
* 解码
*
* @param morse 莫尔斯电码
* @return 明文
*/
public
String
decode
(
String
morse
)
{
Assert
.
notNull
(
morse
,
"Morse should not be null."
);
final
char
dit
=
this
.
dit
;
final
char
dah
=
this
.
dah
;
final
char
split
=
this
.
split
;
if
(
false
==
StrUtil
.
containsOnly
(
morse
,
dit
,
dah
,
split
))
{
throw
new
IllegalArgumentException
(
"Incorrect morse."
);
}
final
List
<
String
>
words
=
StrUtil
.
split
(
morse
,
split
);
final
StringBuilder
textBuilder
=
new
StringBuilder
();
Integer
codePoint
;
for
(
String
word
:
words
)
{
if
(
StrUtil
.
isEmpty
(
word
)){
continue
;
}
word
=
word
.
replace
(
dit
,
'0'
).
replace
(
dah
,
'1'
);
codePoint
=
DICTIONARIES
.
get
(
word
);
if
(
codePoint
==
null
)
{
codePoint
=
Integer
.
valueOf
(
word
,
2
);
}
textBuilder
.
appendCodePoint
(
codePoint
);
}
return
textBuilder
.
toString
();
}
}
Prev
1
…
7
8
9
10
11
12
13
14
15
…
19
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment