├── 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 [![license](https://img.shields.io/github/license/mashape/apistatus.svg)]() 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 | --------------------------------------------------------------------------------