commit 3add1a1098f3c23e356662a9ccb05451f17cf463
Author: ZhuoQinghui <1302344380@qq.com>
Date: Fri Jul 29 16:22:40 2022 +0800
ShortUrl.java
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b425f09
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7cf67ac
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ cn.zzzykj
+ zy-short-url
+ pom
+ 1.0-SNAPSHOT
+
+ zy-shorturl-core
+ zy-shorturl-spring-boot-starter
+
+
+
+ 8
+ 8
+ UTF-8
+
+
+
\ No newline at end of file
diff --git a/zy-shorturl-core/pom.xml b/zy-shorturl-core/pom.xml
new file mode 100644
index 0000000..e5c3b0b
--- /dev/null
+++ b/zy-shorturl-core/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+ zy-short-url
+ cn.zzzykj
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ zy-short-url-core
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ com.google.guava
+ guava
+ 31.1-jre
+ true
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.24
+ compile
+ true
+
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+ true
+
+
+
+
\ No newline at end of file
diff --git a/zy-shorturl-core/src/main/java/cn/shorturl/core/ShortUrl.java b/zy-shorturl-core/src/main/java/cn/shorturl/core/ShortUrl.java
new file mode 100644
index 0000000..6d23866
--- /dev/null
+++ b/zy-shorturl-core/src/main/java/cn/shorturl/core/ShortUrl.java
@@ -0,0 +1,77 @@
+package cn.shorturl.core;
+
+import cn.shorturl.core.enums.HashType;
+import cn.shorturl.core.util.Base62;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author Lenovo
+ */
+@Data
+@Accessors(chain = true)
+public class ShortUrl {
+
+ private ShortUrlConfig config;
+
+ private final HashFunction hashFunction;
+
+ private static final Base62 base62 = Base62.createInstance();
+
+ public ShortUrl() {
+ config = new ShortUrlConfig();
+ hashFunction = getHashFunction(config);
+ }
+
+ public ShortUrl(ShortUrlConfig config) {
+ this.config = config;
+ hashFunction = getHashFunction(config);
+ }
+
+ public String gen(String url) {
+ HashCode hashCode = hashFunction.hashString(url, StandardCharsets.UTF_8);
+ byte[] bytes = hashCode.asBytes();
+ return new String(base62.encode(bytes));
+ }
+
+ private static HashFunction getHashFunction(ShortUrlConfig config) {
+ return getHashFunction(config.getHashType(), config.getHashKey());
+ }
+
+ private static HashFunction getHashFunction(HashType type, String key) {
+ if (type == null) {
+ return Hashing.murmur3_32_fixed();
+ }
+ if (key == null) {
+ key = ShortUrl.class.getName();
+ }
+ byte[] keyBytes = key.getBytes();
+ switch (type) {
+ case MD5: return Hashing.md5();
+ case GOOD_FAST_HASH: return Hashing.goodFastHash(32);
+ case SHA1: return Hashing.sha1();
+ case SHA256: return Hashing.sha256();
+ case SHA384: return Hashing.sha384();
+ case SHA512: return Hashing.sha512();
+ case HMAC_MD5: return Hashing.hmacMd5(keyBytes);
+ case HMAC_SHA1: return Hashing.hmacSha1(keyBytes);
+ case HMAC_SHA256: return Hashing.hmacSha256(keyBytes);
+ case HMAC_SHA512: return Hashing.hmacSha512(keyBytes);
+ case CRC32_C: return Hashing.crc32c();
+ case ADLER32: return Hashing.adler32();
+ case FARM_HASH_FINGERPRINT64: return Hashing.farmHashFingerprint64();
+ case FINGERPRINT2011: return Hashing.fingerprint2011();
+ case MURMUR3_32: return Hashing.murmur3_32();
+ case MURMUR3_128: return Hashing.murmur3_128();
+ case SIP_HASH24: return Hashing.sipHash24();
+ case MURMUR3_32_FIXED:
+ default: return Hashing.murmur3_32_fixed();
+ }
+ }
+
+}
diff --git a/zy-shorturl-core/src/main/java/cn/shorturl/core/ShortUrlConfig.java b/zy-shorturl-core/src/main/java/cn/shorturl/core/ShortUrlConfig.java
new file mode 100644
index 0000000..dd21e9e
--- /dev/null
+++ b/zy-shorturl-core/src/main/java/cn/shorturl/core/ShortUrlConfig.java
@@ -0,0 +1,27 @@
+package cn.shorturl.core;
+
+import cn.shorturl.core.enums.HashType;
+import cn.shorturl.core.enums.StoreType;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * @author Lenovo
+ */
+@Data
+@Accessors(chain = true)
+public class ShortUrlConfig {
+
+ /**
+ * Hash方式
+ */
+ private HashType hashType = HashType.MURMUR3_32_FIXED;
+
+ private String hashKey = ShortUrlConfig.class.getName();
+
+ /**
+ * 缓存类型
+ */
+ private StoreType storeType = StoreType.MEMORY;
+
+}
diff --git a/zy-shorturl-core/src/main/java/cn/shorturl/core/enums/HashType.java b/zy-shorturl-core/src/main/java/cn/shorturl/core/enums/HashType.java
new file mode 100644
index 0000000..273db77
--- /dev/null
+++ b/zy-shorturl-core/src/main/java/cn/shorturl/core/enums/HashType.java
@@ -0,0 +1,48 @@
+package cn.shorturl.core.enums;
+
+/**
+ * @author Lenovo
+ */
+
+public enum HashType {
+
+ /**
+ * MD5
+ */
+ MD5,
+
+ GOOD_FAST_HASH,
+
+ SHA1,
+
+ SHA256,
+
+ SHA384,
+
+ SHA512,
+
+ HMAC_MD5,
+
+ HMAC_SHA1,
+
+ HMAC_SHA256,
+
+ HMAC_SHA512,
+
+ CRC32_C,
+
+ ADLER32,
+
+ FARM_HASH_FINGERPRINT64,
+
+ FINGERPRINT2011,
+
+ MURMUR3_32,
+
+ MURMUR3_32_FIXED,
+
+ MURMUR3_128,
+
+ SIP_HASH24;
+
+}
diff --git a/zy-shorturl-core/src/main/java/cn/shorturl/core/enums/StoreType.java b/zy-shorturl-core/src/main/java/cn/shorturl/core/enums/StoreType.java
new file mode 100644
index 0000000..9c5cb35
--- /dev/null
+++ b/zy-shorturl-core/src/main/java/cn/shorturl/core/enums/StoreType.java
@@ -0,0 +1,34 @@
+package cn.shorturl.core.enums;
+
+/**
+ * data store type
+ * @author Lenovo
+ */
+public enum StoreType {
+
+ /**
+ * mysql, sqlserver, oracle 等支持jdbc的数据库
+ */
+ JDBC,
+
+ /**
+ * redis
+ */
+ REDIS,
+
+ /**
+ * mongodb
+ */
+ MONGODB,
+
+ /**
+ * Hazelcast
+ */
+ HAZELCAST,
+
+ /**
+ * 内存
+ */
+ MEMORY,
+
+}
diff --git a/zy-shorturl-core/src/main/java/cn/shorturl/core/util/Base62.java b/zy-shorturl-core/src/main/java/cn/shorturl/core/util/Base62.java
new file mode 100644
index 0000000..0edf669
--- /dev/null
+++ b/zy-shorturl-core/src/main/java/cn/shorturl/core/util/Base62.java
@@ -0,0 +1,223 @@
+package cn.shorturl.core.util;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Base62编码解码实现,常用于短URL
+ * From https://github.com/seruco/base62
+ * A Base62 encoder/decoder.
+ *
+ * @author Sebastian Ruhleder, sebastian@seruco.io
+ */
+public class Base62 {
+
+ private static final int STANDARD_BASE = 256;
+
+ private static final int TARGET_BASE = 62;
+
+ private final byte[] alphabet;
+
+ private byte[] lookup;
+
+ private Base62(final byte[] alphabet) {
+ this.alphabet = alphabet;
+ createLookupTable();
+ }
+
+ /**
+ * Creates a {@link Base62} instance. Defaults to the GMP-style character set.
+ *
+ * @return a {@link Base62} instance.
+ */
+ public static Base62 createInstance() {
+ return createInstanceWithGmpCharacterSet();
+ }
+
+ /**
+ * Creates a {@link Base62} instance using the GMP-style character set.
+ *
+ * @return a {@link Base62} instance.
+ */
+ public static Base62 createInstanceWithGmpCharacterSet() {
+ return new Base62(CharacterSets.GMP);
+ }
+
+ /**
+ * Creates a {@link Base62} instance using the inverted character set.
+ *
+ * @return a {@link Base62} instance.
+ */
+ public static Base62 createInstanceWithInvertedCharacterSet() {
+ return new Base62(CharacterSets.INVERTED);
+ }
+
+ /**
+ * Encodes a sequence of bytes in Base62 encoding.
+ *
+ * @param message a byte sequence.
+ * @return a sequence of Base62-encoded bytes.
+ */
+ public byte[] encode(final byte[] message) {
+ final byte[] indices = convert(message, STANDARD_BASE, TARGET_BASE);
+
+ return translate(indices, alphabet);
+ }
+
+ /**
+ * Decodes a sequence of Base62-encoded bytes.
+ *
+ * @param encoded a sequence of Base62-encoded bytes.
+ * @return a byte sequence.
+ * @throws IllegalArgumentException when {@code encoded} is not encoded over the Base62 alphabet.
+ */
+ public byte[] decode(final byte[] encoded) {
+ if (!isBase62Encoding(encoded)) {
+ throw new IllegalArgumentException("Input is not encoded correctly");
+ }
+
+ final byte[] prepared = translate(encoded, lookup);
+
+ return convert(prepared, TARGET_BASE, STANDARD_BASE);
+ }
+
+ /**
+ * Checks whether a sequence of bytes is encoded over a Base62 alphabet.
+ *
+ * @param bytes a sequence of bytes.
+ * @return {@code true} when the bytes are encoded over a Base62 alphabet, {@code false} otherwise.
+ */
+ public boolean isBase62Encoding(final byte[] bytes) {
+ if (bytes == null) {
+ return false;
+ }
+
+ for (final byte e : bytes) {
+ if ('0' > e || '9' < e) {
+ if ('a' > e || 'z' < e) {
+ if ('A' > e || 'Z' < e) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Uses the elements of a byte array as indices to a dictionary and returns the corresponding values
+ * in form of a byte array.
+ */
+ private byte[] translate(final byte[] indices, final byte[] dictionary) {
+ final byte[] translation = new byte[indices.length];
+
+ for (int i = 0; i < indices.length; i++) {
+ translation[i] = dictionary[indices[i]];
+ }
+
+ return translation;
+ }
+
+ /**
+ * Converts a byte array from a source base to a target base using the alphabet.
+ */
+ private byte[] convert(final byte[] message, final int sourceBase, final int targetBase) {
+ /**
+ * This algorithm is inspired by: 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 (int i = 0; i < source.length; i++) {
+ final int accumulator = (source[i] & 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 reverse(out.toByteArray());
+ }
+
+ /**
+ * Estimates the length of the output in bytes.
+ */
+ private int estimateOutputLength(int inputLength, int sourceBase, int targetBase) {
+ return (int) Math.ceil((Math.log(sourceBase) / Math.log(targetBase)) * inputLength);
+ }
+
+ /**
+ * Reverses a byte array.
+ */
+ private byte[] reverse(final byte[] arr) {
+ final int length = arr.length;
+
+ final byte[] reversed = new byte[length];
+
+ for (int i = 0; i < length; i++) {
+ reversed[length - i - 1] = arr[i];
+ }
+
+ return reversed;
+ }
+
+ /**
+ * Creates the lookup table from character to index of character in character set.
+ */
+ private void createLookupTable() {
+ lookup = new byte[256];
+
+ for (int i = 0; i < alphabet.length; i++) {
+ lookup[alphabet[i]] = (byte) (i & 0xFF);
+ }
+ }
+
+ private static class CharacterSets {
+
+ private static final byte[] GMP = {
+ (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
+ (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
+ (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
+ (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
+ (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
+ (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
+ (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
+ (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z'
+ };
+
+ private static final byte[] INVERTED = {
+ (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
+ (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
+ (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
+ (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
+ (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
+ (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
+ (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
+ (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z'
+ };
+
+ }
+
+}
diff --git a/zy-shorturl-core/src/test/java/cn/shorturl/core/ShortUrlTest.java b/zy-shorturl-core/src/test/java/cn/shorturl/core/ShortUrlTest.java
new file mode 100644
index 0000000..967e1c1
--- /dev/null
+++ b/zy-shorturl-core/src/test/java/cn/shorturl/core/ShortUrlTest.java
@@ -0,0 +1,26 @@
+package cn.shorturl.core;
+
+import cn.shorturl.core.enums.HashType;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ShortUrlTest {
+
+ ShortUrlConfig config;
+
+ ShortUrl shortUrl;
+
+ @Before
+ public void init() {
+ config = new ShortUrlConfig().setHashType(HashType.MURMUR3_32_FIXED);
+ shortUrl = new ShortUrl(config);
+ }
+
+ @Test
+ public void testGen() {
+ String url = "http://www.baidu.com//?asdasdasdaas=das==wqe===gfa=sd=asf=g=eq=w===a=sd=as=fg=w=d=as=d=as=f=w=";
+ String gen = shortUrl.gen(url);
+ System.out.println(gen);
+ }
+
+}
\ No newline at end of file
diff --git a/zy-shorturl-spring-boot-starter/pom.xml b/zy-shorturl-spring-boot-starter/pom.xml
new file mode 100644
index 0000000..90cf7e5
--- /dev/null
+++ b/zy-shorturl-spring-boot-starter/pom.xml
@@ -0,0 +1,20 @@
+
+
+
+ zy-short-url
+ cn.zzzykj
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ zy-short-url-spring-boot-starter
+
+
+ 8
+ 8
+ UTF-8
+
+
+
\ No newline at end of file