├── LICENSE
├── README.md
├── src
├── test
│ └── java
│ │ └── io
│ │ └── seruco
│ │ └── encoding
│ │ └── base62
│ │ ├── Environment.java
│ │ └── Base62Test.java
└── main
│ └── java
│ └── io
│ └── seruco
│ └── encoding
│ └── base62
│ └── Base62.java
├── .gitignore
└── pom.xml
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Sebastian Ruhleder
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Base62 []()
2 |
3 | **A Base62 Encoder/Decoder for Java**
4 |
5 | ## Getting Started
6 |
7 | For Maven-based projects, add the following to your `pom.xml` file. This dependency is available from the Maven Central repository.
8 |
9 | ```xml
10 |
11 | io.seruco.encoding
12 | base62
13 | 0.1.3
14 |
15 | ```
16 |
17 | ## Usage
18 |
19 | ```java
20 | Base62 base62 = Base62.createInstance();
21 | ```
22 |
23 | ### Encoding
24 |
25 | ```java
26 | final byte[] encoded = base62.encode("Hello World".getBytes());
27 |
28 | new String(encoded); // is "73XpUgyMwkGr29M"
29 | ```
30 |
31 | ### Decoding
32 |
33 | ```java
34 | final byte[] decoded = base62.decode("73XpUgyMwkGr29M".getBytes());
35 |
36 | new String(decoded); // is "Hello World"
37 | ```
38 |
39 | ## Character Sets
40 |
41 | This library supports two character sets: GMP-style or inverted. The difference between these two is whether the upper case letters come first, `0-9A-Za-z` (GMP), or last, `0-9a-zA-Z` (inverted).
42 |
43 | By default, we prefer the GMP-style character set. If you want to use the inverted character set, simply do this:
44 |
45 | ```java
46 | Base62 base62 = Base62.createInstanceWithInvertedCharacterSet();
47 | ```
48 |
49 | ## Licensing
50 |
51 | This project is licensed under the [MIT License](LICENSE).
--------------------------------------------------------------------------------
/src/test/java/io/seruco/encoding/base62/Environment.java:
--------------------------------------------------------------------------------
1 | package io.seruco.encoding.base62;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public class Environment {
7 | public static final byte[][] getRawInputs() {
8 | return new byte[][]{
9 | createIncreasingByteArray(),
10 | createZeroesByteArray(1),
11 | createZeroesByteArray(512),
12 | createPseudoRandomByteArray(0xAB, 40),
13 | createPseudoRandomByteArray(0x1C, 40),
14 | createPseudoRandomByteArray(0xF2, 40)
15 | };
16 | }
17 |
18 | public static Map getNaiveTestSet() {
19 | final Map testSet = new HashMap();
20 |
21 | testSet.put("", "");
22 | testSet.put("a", "1Z");
23 | testSet.put("Hello", "5TP3P3v");
24 | testSet.put("Hello world!", "T8dgcjRGuYUueWht");
25 | testSet.put("Just a test", "7G0iTmJjQFG2t6K");
26 | testSet.put("!!!!!!!!!!!!!!!!!", "4A7f43EVXQoS6Am897ZKbAn");
27 | testSet.put("0123456789", "18XU2xYejWO9d3");
28 | testSet.put("The quick brown fox jumps over the lazy dog", "83UM8dOjD4xrzASgmqLOXTgTagvV1jPegUJ39mcYnwHwTlzpdfKXvpp4RL");
29 | testSet.put("Sphinx of black quartz, judge my vow", "1Ul5yQGNM8YFBp3sz19dYj1kTp95OW7jI8pTcTP5JhYjIaFmx");
30 |
31 | return testSet;
32 | }
33 |
34 | public static final byte[][] getWrongEncoding() {
35 | return new byte[][]{
36 | "&".getBytes(),
37 | "abcde$".getBytes(),
38 | "()".getBytes(),
39 | "\uD83D\uDE31".getBytes()
40 | };
41 | }
42 |
43 | private static byte[] createIncreasingByteArray() {
44 | final byte[] arr = new byte[256];
45 | for (int i = 0; i < 256; i++) {
46 | arr[i] = (byte) (i & 0xFF);
47 | }
48 | return arr;
49 | }
50 |
51 | private static byte[] createZeroesByteArray(int size) {
52 | return new byte[size];
53 | }
54 |
55 | private static byte[] createPseudoRandomByteArray(int seed, int size) {
56 | final byte[] arr = new byte[size];
57 | int state = seed;
58 | for (int i = 0; i < size; i += 4) {
59 | state = xorshift(state);
60 | for (int j = 0; j < 4 && i + j < size; j++) {
61 | arr[i + j] = (byte) ((state >> j) & 0xFF);
62 | }
63 | }
64 | return arr;
65 | }
66 |
67 | private static int xorshift(int x) {
68 | x ^= (x << 13);
69 | x ^= (x >> 17);
70 | x ^= (x << 5);
71 | return x;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/maven,eclipse,netbeans,intellij+all
3 |
4 | ### Eclipse ###
5 |
6 | .metadata
7 | bin/
8 | tmp/
9 | *.tmp
10 | *.bak
11 | *.swp
12 | *~.nib
13 | local.properties
14 | .settings/
15 | .loadpath
16 | .recommenders
17 |
18 | # External tool builders
19 | .externalToolBuilders/
20 |
21 | # Locally stored "Eclipse launch configurations"
22 | *.launch
23 |
24 | # PyDev specific (Python IDE for Eclipse)
25 | *.pydevproject
26 |
27 | # CDT-specific (C/C++ Development Tooling)
28 | .cproject
29 |
30 | # Java annotation processor (APT)
31 | .factorypath
32 |
33 | # PDT-specific (PHP Development Tools)
34 | .buildpath
35 |
36 | # sbteclipse plugin
37 | .target
38 |
39 | # Tern plugin
40 | .tern-project
41 |
42 | # TeXlipse plugin
43 | .texlipse
44 |
45 | # STS (Spring Tool Suite)
46 | .springBeans
47 |
48 | # Code Recommenders
49 | .recommenders/
50 |
51 | # Scala IDE specific (Scala & Java development for Eclipse)
52 | .cache-main
53 | .scala_dependencies
54 | .worksheet
55 |
56 | ### Eclipse Patch ###
57 | # Eclipse Core
58 | .project
59 |
60 | # JDT-specific (Eclipse Java Development Tools)
61 | .classpath
62 |
63 | ### Intellij+all ###
64 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
65 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
66 |
67 | # User-specific stuff:
68 | .idea/**/workspace.xml
69 | .idea/**/tasks.xml
70 | .idea/dictionaries
71 |
72 | # Sensitive or high-churn files:
73 | .idea/**/dataSources/
74 | .idea/**/dataSources.ids
75 | .idea/**/dataSources.xml
76 | .idea/**/dataSources.local.xml
77 | .idea/**/sqlDataSources.xml
78 | .idea/**/dynamic.xml
79 | .idea/**/uiDesigner.xml
80 |
81 | # Gradle:
82 | .idea/**/gradle.xml
83 | .idea/**/libraries
84 |
85 | # CMake
86 | cmake-build-debug/
87 |
88 | # Mongo Explorer plugin:
89 | .idea/**/mongoSettings.xml
90 |
91 | ## File-based project format:
92 | *.iws
93 |
94 | ## Plugin-specific files:
95 |
96 | # IntelliJ
97 | /out/
98 |
99 | # mpeltonen/sbt-idea plugin
100 | .idea_modules/
101 |
102 | # JIRA plugin
103 | atlassian-ide-plugin.xml
104 |
105 | # Cursive Clojure plugin
106 | .idea/replstate.xml
107 |
108 | # Ruby plugin and RubyMine
109 | /.rakeTasks
110 |
111 | # Crashlytics plugin (for Android Studio and IntelliJ)
112 | com_crashlytics_export_strings.xml
113 | crashlytics.properties
114 | crashlytics-build.properties
115 | fabric.properties
116 |
117 | ### Intellij+all Patch ###
118 | # Ignores the whole idea folder
119 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
120 |
121 | .idea/
122 |
123 | ### Maven ###
124 | target/
125 | pom.xml.tag
126 | pom.xml.releaseBackup
127 | pom.xml.versionsBackup
128 | pom.xml.next
129 | release.properties
130 | dependency-reduced-pom.xml
131 | buildNumber.properties
132 | .mvn/timing.properties
133 |
134 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
135 | !/.mvn/wrapper/maven-wrapper.jar
136 |
137 | ### NetBeans ###
138 | nbproject/private/
139 | build/
140 | nbbuild/
141 | dist/
142 | nbdist/
143 | .nb-gradle/
144 |
145 | # End of https://www.gitignore.io/api/maven,eclipse,netbeans,intellij+all
146 |
147 | *.iml
148 |
149 | travis/GPG_KEY.asc
150 |
--------------------------------------------------------------------------------
/src/test/java/io/seruco/encoding/base62/Base62Test.java:
--------------------------------------------------------------------------------
1 | package io.seruco.encoding.base62;
2 |
3 | import org.junit.jupiter.api.DisplayName;
4 | import org.junit.jupiter.api.Test;
5 | import org.junit.jupiter.api.function.Executable;
6 |
7 | import java.util.Map;
8 |
9 | import static org.junit.jupiter.api.Assertions.assertArrayEquals;
10 | import static org.junit.jupiter.api.Assertions.assertEquals;
11 | import static org.junit.jupiter.api.Assertions.assertFalse;
12 | import static org.junit.jupiter.api.Assertions.assertThrows;
13 | import static org.junit.jupiter.api.Assertions.assertTrue;
14 |
15 | @DisplayName("Base62")
16 | public class Base62Test {
17 |
18 | private final Base62 standardEncoder = Base62.createInstance();
19 |
20 | private final Base62[] encoders = {
21 | Base62.createInstanceWithGmpCharacterSet(),
22 | Base62.createInstanceWithInvertedCharacterSet()
23 | };
24 |
25 | @Test
26 | @DisplayName("should preserve identity of simple byte arrays")
27 | public void preservesIdentity() {
28 | for (byte[] message : Environment.getRawInputs()) {
29 | for (Base62 encoder : encoders) {
30 | final byte[] encoded = encoder.encode(message);
31 | final byte[] decoded = encoder.decode(encoded);
32 |
33 | assertArrayEquals(message, decoded);
34 | }
35 | }
36 | }
37 |
38 | @Test
39 | @DisplayName("should produce encodings that only contain alphanumeric characters")
40 | public void alphaNumericOutput() {
41 | for (byte[] message : Environment.getRawInputs()) {
42 | for (Base62 encoder : encoders) {
43 | final byte[] encoded = encoder.encode(message);
44 | final String encodedStr = new String(encoded);
45 |
46 | assertTrue(isAlphaNumeric(encodedStr));
47 | }
48 | }
49 | }
50 |
51 | @Test
52 | @DisplayName("should be able to handle empty inputs")
53 | public void emptyInputs() {
54 | final byte[] empty = new byte[0];
55 |
56 | for (Base62 encoder : encoders) {
57 | final byte[] encoded = encoder.encode(empty);
58 | assertArrayEquals(empty, encoded);
59 |
60 | final byte[] decoded = encoder.decode(empty);
61 | assertArrayEquals(empty, decoded);
62 | }
63 | }
64 |
65 | @Test
66 | @DisplayName("should behave correctly on naive test set")
67 | public void naiveTestSet() {
68 | for (Map.Entry testSetEntry : Environment.getNaiveTestSet().entrySet()) {
69 | assertEquals(encode(testSetEntry.getKey()), testSetEntry.getValue());
70 | }
71 | }
72 |
73 | @Test
74 | @DisplayName("should throw exception when input is not encoded correctly")
75 | public void wrongEncoding() {
76 | for (final byte[] input : Environment.getWrongEncoding()) {
77 | assertThrows(IllegalArgumentException.class, new Executable() {
78 | @Override
79 | public void execute() throws Throwable {
80 | standardEncoder.decode(input);
81 | }
82 | });
83 | }
84 | }
85 |
86 | @Test
87 | @DisplayName("should throw exception when input is null when decoding")
88 | public void decodeNull() {
89 | assertThrows(IllegalArgumentException.class, new Executable() {
90 | @Override
91 | public void execute() throws Throwable {
92 | standardEncoder.decode(null);
93 | }
94 | });
95 | }
96 |
97 | @Test
98 | @DisplayName("should check encoding correctly")
99 | public void checkEncoding() {
100 | assertTrue(standardEncoder.isBase62Encoding("0123456789".getBytes()));
101 | assertTrue(standardEncoder.isBase62Encoding("abcdefghijklmnopqrstuvwxzy".getBytes()));
102 | assertTrue(standardEncoder.isBase62Encoding("ABCDEFGHIJKLMNOPQRSTUVWXZY".getBytes()));
103 |
104 | assertFalse(standardEncoder.isBase62Encoding("!".getBytes()));
105 | assertFalse(standardEncoder.isBase62Encoding("@".getBytes()));
106 | assertFalse(standardEncoder.isBase62Encoding("<>".getBytes()));
107 | assertFalse(standardEncoder.isBase62Encoding("abcd%".getBytes()));
108 | assertFalse(standardEncoder.isBase62Encoding("😱".getBytes()));
109 | }
110 |
111 | private String encode(final String input) {
112 | return new String(standardEncoder.encode(input.getBytes()));
113 | }
114 |
115 | private boolean isAlphaNumeric(final String str) {
116 | return str.matches("^[a-zA-Z0-9]+$");
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | io.seruco.encoding
8 | base62
9 | 0.1.3
10 | jar
11 |
12 | Base62
13 | A Base62 encoder/decoder for Java
14 | https://github.com/seruco/base62
15 |
16 |
17 |
18 | MIT
19 | https://opensource.org/licenses/MIT
20 |
21 |
22 |
23 |
24 |
25 | Sebastian Ruhleder
26 | sebastian@seruco.io
27 | Seruco
28 | http://seruco.io
29 |
30 |
31 |
32 |
33 | https://github.com/seruco/base62.git
34 | scm:git:git@github.com:seruco/base62.git
35 | scm:git:git@github.com:seruco/base62.git
36 |
37 |
38 |
39 | 1.8
40 | 1.8
41 | UTF-8
42 | UTF-8
43 | 2.19.1
44 | 3.0.2
45 | 3.0.0-M1
46 | 1.6
47 | 5.0.2
48 | 4.12.2
49 | 1.0.2
50 |
51 |
52 |
53 |
54 | org.junit.jupiter
55 | junit-jupiter-api
56 | ${junit-jupiter.version}
57 | test
58 |
59 |
60 | org.junit.jupiter
61 | junit-jupiter-engine
62 | ${junit-jupiter.version}
63 | test
64 |
65 |
66 | org.junit.vintage
67 | junit-vintage-engine
68 | ${junit-vintage.version}
69 | test
70 |
71 |
72 | org.junit.platform
73 | junit-platform-launcher
74 | ${junit-platform.version}
75 | test
76 |
77 |
78 | org.junit.platform
79 | junit-platform-runner
80 | ${junit-platform.version}
81 | test
82 |
83 |
84 |
85 |
86 | ${project.basedir}/src/main/java
87 | ${project.basedir}/src/test/java
88 |
89 |
90 | ${project.basedir}/src/test/resources
91 |
92 |
93 |
94 |
95 | org.apache.maven.plugins
96 | maven-surefire-plugin
97 | ${maven-surefire-plugin.version}
98 |
99 |
100 | org.junit.platform
101 | junit-platform-surefire-provider
102 | ${junit-platform.version}
103 |
104 |
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-jar-plugin
109 | ${maven-jar-plugin.version}
110 |
111 |
112 |
113 | io.seruco.encoding.base62
114 |
115 |
116 |
117 |
118 |
119 | org.apache.maven.plugins
120 | maven-javadoc-plugin
121 | ${maven-javadoc-plugin.version}
122 |
123 |
124 | org.apache.maven.plugins
125 | maven-gpg-plugin
126 | ${maven-gpg-plugin.version}
127 |
128 |
129 | sign-artifacts
130 | verify
131 |
132 | sign
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | ossrh
143 | https://oss.sonatype.org/content/repositories/snapshots
144 |
145 |
146 | ossrh
147 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
148 |
149 |
150 |
--------------------------------------------------------------------------------
/src/main/java/io/seruco/encoding/base62/Base62.java:
--------------------------------------------------------------------------------
1 | package io.seruco.encoding.base62;
2 |
3 | import java.io.ByteArrayOutputStream;
4 |
5 | /**
6 | * A Base62 encoder/decoder.
7 | *
8 | * @author Sebastian Ruhleder, sebastian@seruco.io
9 | */
10 | public class Base62 {
11 |
12 | private static final int STANDARD_BASE = 256;
13 |
14 | private static final int TARGET_BASE = 62;
15 |
16 | private final byte[] alphabet;
17 |
18 | private byte[] lookup;
19 |
20 | private Base62(final byte[] alphabet) {
21 | this.alphabet = alphabet;
22 | createLookupTable();
23 | }
24 |
25 | /**
26 | * Creates a {@link Base62} instance. Defaults to the GMP-style character set.
27 | *
28 | * @return a {@link Base62} instance.
29 | */
30 | public static Base62 createInstance() {
31 | return createInstanceWithGmpCharacterSet();
32 | }
33 |
34 | /**
35 | * Creates a {@link Base62} instance using the GMP-style character set.
36 | *
37 | * @return a {@link Base62} instance.
38 | */
39 | public static Base62 createInstanceWithGmpCharacterSet() {
40 | return new Base62(CharacterSets.GMP);
41 | }
42 |
43 | /**
44 | * Creates a {@link Base62} instance using the inverted character set.
45 | *
46 | * @return a {@link Base62} instance.
47 | */
48 | public static Base62 createInstanceWithInvertedCharacterSet() {
49 | return new Base62(CharacterSets.INVERTED);
50 | }
51 |
52 | /**
53 | * Encodes a sequence of bytes in Base62 encoding.
54 | *
55 | * @param message a byte sequence.
56 | * @return a sequence of Base62-encoded bytes.
57 | */
58 | public byte[] encode(final byte[] message) {
59 | final byte[] indices = convert(message, STANDARD_BASE, TARGET_BASE);
60 |
61 | return translate(indices, alphabet);
62 | }
63 |
64 | /**
65 | * Decodes a sequence of Base62-encoded bytes.
66 | *
67 | * @param encoded a sequence of Base62-encoded bytes.
68 | * @return a byte sequence.
69 | * @throws IllegalArgumentException when {@code encoded} is not encoded over the Base62 alphabet.
70 | */
71 | public byte[] decode(final byte[] encoded) {
72 | if (!isBase62Encoding(encoded)) {
73 | throw new IllegalArgumentException("Input is not encoded correctly");
74 | }
75 |
76 | final byte[] prepared = translate(encoded, lookup);
77 |
78 | return convert(prepared, TARGET_BASE, STANDARD_BASE);
79 | }
80 |
81 | /**
82 | * Checks whether a sequence of bytes is encoded over a Base62 alphabet.
83 | *
84 | * @param bytes a sequence of bytes.
85 | * @return {@code true} when the bytes are encoded over a Base62 alphabet, {@code false} otherwise.
86 | */
87 | public boolean isBase62Encoding(final byte[] bytes) {
88 | if (bytes == null) {
89 | return false;
90 | }
91 |
92 | for (final byte e : bytes) {
93 | if ('0' > e || '9' < e) {
94 | if ('a' > e || 'z' < e) {
95 | if ('A' > e || 'Z' < e) {
96 | return false;
97 | }
98 | }
99 | }
100 | }
101 |
102 | return true;
103 | }
104 |
105 | /**
106 | * Uses the elements of a byte array as indices to a dictionary and returns the corresponding values
107 | * in form of a byte array.
108 | */
109 | private byte[] translate(final byte[] indices, final byte[] dictionary) {
110 | final byte[] translation = new byte[indices.length];
111 |
112 | for (int i = 0; i < indices.length; i++) {
113 | translation[i] = dictionary[indices[i]];
114 | }
115 |
116 | return translation;
117 | }
118 |
119 | /**
120 | * Converts a byte array from a source base to a target base using the alphabet.
121 | */
122 | private byte[] convert(final byte[] message, final int sourceBase, final int targetBase) {
123 | /**
124 | * This algorithm is inspired by: http://codegolf.stackexchange.com/a/21672
125 | */
126 |
127 | final int estimatedLength = estimateOutputLength(message.length, sourceBase, targetBase);
128 |
129 | final ByteArrayOutputStream out = new ByteArrayOutputStream(estimatedLength);
130 |
131 | byte[] source = message;
132 |
133 | while (source.length > 0) {
134 | final ByteArrayOutputStream quotient = new ByteArrayOutputStream(source.length);
135 |
136 | int remainder = 0;
137 |
138 | for (int i = 0; i < source.length; i++) {
139 | final int accumulator = (source[i] & 0xFF) + remainder * sourceBase;
140 | final int digit = (accumulator - (accumulator % targetBase)) / targetBase;
141 |
142 | remainder = accumulator % targetBase;
143 |
144 | if (quotient.size() > 0 || digit > 0) {
145 | quotient.write(digit);
146 | }
147 | }
148 |
149 | out.write(remainder);
150 |
151 | source = quotient.toByteArray();
152 | }
153 |
154 | // pad output with zeroes corresponding to the number of leading zeroes in the message
155 | for (int i = 0; i < message.length - 1 && message[i] == 0; i++) {
156 | out.write(0);
157 | }
158 |
159 | return reverse(out.toByteArray());
160 | }
161 |
162 | /**
163 | * Estimates the length of the output in bytes.
164 | */
165 | private int estimateOutputLength(int inputLength, int sourceBase, int targetBase) {
166 | return (int) Math.ceil((Math.log(sourceBase) / Math.log(targetBase)) * inputLength);
167 | }
168 |
169 | /**
170 | * Reverses a byte array.
171 | */
172 | private byte[] reverse(final byte[] arr) {
173 | final int length = arr.length;
174 |
175 | final byte[] reversed = new byte[length];
176 |
177 | for (int i = 0; i < length; i++) {
178 | reversed[length - i - 1] = arr[i];
179 | }
180 |
181 | return reversed;
182 | }
183 |
184 | /**
185 | * Creates the lookup table from character to index of character in character set.
186 | */
187 | private void createLookupTable() {
188 | lookup = new byte[256];
189 |
190 | for (int i = 0; i < alphabet.length; i++) {
191 | lookup[alphabet[i]] = (byte) (i & 0xFF);
192 | }
193 | }
194 |
195 | private static class CharacterSets {
196 |
197 | private static final byte[] GMP = {
198 | (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
199 | (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
200 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
201 | (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
202 | (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
203 | (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
204 | (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
205 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z'
206 | };
207 |
208 | private static final byte[] INVERTED = {
209 | (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
210 | (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
211 | (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
212 | (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
213 | (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
214 | (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
215 | (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
216 | (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z'
217 | };
218 |
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------