From 9b16947508df453bdcd0b79ffe4f3146b35ac5da Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 20 Mar 2022 21:20:51 +0800 Subject: [PATCH] add Base62 Inverted --- CHANGELOG.md | 6 +- .../java/cn/hutool/core/codec/Base62.java | 129 +++++++++++- .../cn/hutool/core/codec/Base62Codec.java | 190 +++++++++++------- .../java/cn/hutool/core/codec/Base62Test.java | 27 +++ 4 files changed, 272 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70f423188..47b286e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,14 @@ # 5.8.0 (2022-03-20) ### ❌不兼容特性 -* 【db 】 【不向下兼容】增加MongoDB4.x支持(pr#568@Gitee) +* 【db 】 【不向下兼容 】增加MongoDB4.x支持返回MongoClient变更(pr#568@Gitee) * 【json 】 【可能兼容问题】修改JSONObject结构,继承自MapWrapper * 【core 】 【可能兼容问题】BeanCopier重构,新建XXXCopier,删除XXXValueProvider * 【core 】 【可能兼容问题】URLEncoder废弃,URLEncoderUtil使用RFC3986 * 【core 】 【可能兼容问题】Base32分离编码和解码,以便减少数据加载,支持Hex模式 -* 【core 】 【不兼容问题】PunyCode参数由String改为Charsequence +* 【core 】 【可能兼容问题】Base58分离编码和解码 +* 【core 】 【可能兼容问题】Base62分离编码和解码,增加inverted模式支持 +* 【core 】 【兼容问题 】PunyCode参数由String改为Charsequence ### 🐣新特性 * 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base62.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base62.java index bb0454540..9f8a458b4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Base62.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base62.java @@ -1,15 +1,15 @@ package cn.hutool.core.codec; -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.Charset; - 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的编码和解码方案
* @@ -19,7 +19,6 @@ import cn.hutool.core.util.StrUtil; public class Base62 { private static final Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8; - private static final Base62Codec CODEC = Base62Codec.createGmp(); // -------------------------------------------------------------------- encode /** @@ -50,7 +49,7 @@ public class Base62 { * @return 被加密后的字符串 */ public static String encode(byte[] source) { - return new String(CODEC.encode(source)); + return new String(Base62Codec.INSTANCE.encode(source)); } /** @@ -73,6 +72,57 @@ public class Base62 { 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解码 @@ -144,6 +194,69 @@ public class Base62 { * @return 解码后的bytes */ public static byte[] decode(byte[] base62bytes) { - return CODEC.decode(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); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/codec/Base62Codec.java b/hutool-core/src/main/java/cn/hutool/core/codec/Base62Codec.java index 1272406ac..055800dd7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/codec/Base62Codec.java +++ b/hutool-core/src/main/java/cn/hutool/core/codec/Base62Codec.java @@ -12,73 +12,13 @@ import java.io.Serializable; * @author Looly, Sebastian Ruhleder, sebastian@seruco.io * @since 4.5.9 */ -public class Base62Codec implements Encoder, Decoder, Serializable{ +public class Base62Codec implements Encoder, Decoder, Serializable { private static final long serialVersionUID = 1L; private static final int STANDARD_BASE = 256; private static final int TARGET_BASE = 62; - /** - * 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' // - }; - - /** - * 创建GMP风格的Base62编码解码器对象 - * - * @return Base62Codec - */ - public static Base62Codec createGmp() { - return new Base62Codec(GMP); - } - - /** - * 创建Inverted风格的Base62编码解码器对象 - * - * @return Base62Codec - */ - public static Base62Codec createInverted() { - return new Base62Codec(INVERTED); - } - - private final byte[] alphabet; - private final byte[] lookup; - - /** - * 构造 - * - * @param alphabet 自定义字母表 - */ - public Base62Codec(byte[] alphabet) { - this.alphabet = alphabet; - lookup = new byte[256]; - for (int i = 0; i < alphabet.length; i++) { - lookup[alphabet[i]] = (byte) (i & 0xFF); - } - } + public static Base62Codec INSTANCE = new Base62Codec(); /** * 编码指定消息bytes为Base62格式的bytes @@ -88,8 +28,19 @@ public class Base62Codec implements Encoder, Decoder, Decoder { + /** + * 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 { + + 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 indices 内容 * @param dictionary 字典 * @return 转换值 */ @@ -125,7 +175,7 @@ public class Base62Codec implements Encoder, Decoder, Decoder