View Javadoc
1   package com.jsql.util.bruter;
2   
3   import java.nio.charset.StandardCharsets;
4   import java.util.Arrays;
5   
6   public class Base58 {
7   
8       private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
9       private static final int BASE_58 = ALPHABET.length;
10      private static final int BASE_256 = 256;
11  
12      private static final int[] INDEXES = new int[128];
13      
14      static {
15          Arrays.fill(INDEXES, -1);
16          
17          for (int i = 0; i < ALPHABET.length; i++) {
18              INDEXES[ALPHABET[i]] = i;
19          }
20      }
21  
22      private Base58() {
23          // Utility class
24      }
25  
26      public static String encode(byte[] input) {
27          
28          if (input.length == 0) {
29              // paying with the same coin
30              return "";
31          }
32  
33          // Make a copy of the input since we are going to modify it.
34          byte[] copyInput = copyOfRange(input, 0, input.length);
35  
36          // Count leading zeroes
37          var zeroCount = 0;
38          while (zeroCount < copyInput.length && copyInput[zeroCount] == 0) {
39              ++zeroCount;
40          }
41  
42          // The actual encoding
43          var temp = new byte[copyInput.length * 2];
44          int j = temp.length;
45  
46          int startAt = zeroCount;
47          while (startAt < copyInput.length) {
48              
49              byte mod = divmod58(copyInput, startAt);
50              if (copyInput[startAt] == 0) {
51                  ++startAt;
52              }
53  
54              temp[--j] = (byte) ALPHABET[mod];
55          }
56  
57          // Strip extra '1' if any
58          while (j < temp.length && temp[j] == ALPHABET[0]) {
59              ++j;
60          }
61  
62          // Add as many leading '1' as there were leading zeros.
63          while (--zeroCount >= 0) {
64              temp[--j] = (byte) ALPHABET[0];
65          }
66  
67          byte[] output = copyOfRange(temp, j, temp.length);
68          
69          return new String(output, StandardCharsets.UTF_8);
70      }
71  
72      public static byte[] decode(String input) {
73          
74          if (input.isEmpty()) {
75              // paying with the same coin
76              return new byte[0];
77          }
78  
79          var input58 = new byte[input.length()];
80          
81          // Transform the String to a base58 byte sequence
82          for (var i = 0; i < input.length(); ++i) {
83              
84              var c = input.charAt(i);
85  
86              int digit58 = -1;
87              if (c >= 0 && c < 128) {
88                  digit58 = INDEXES[c];
89              }
90              
91              if (digit58 < 0) {
92                  throw new IllegalArgumentException("Not a Base58 input: " + input);
93              }
94  
95              input58[i] = (byte) digit58;
96          }
97  
98          // Count leading zeroes
99          var zeroCount = 0;
100         while (zeroCount < input58.length && input58[zeroCount] == 0) {
101             ++zeroCount;
102         }
103 
104         // The encoding
105         var temp = new byte[input.length()];
106         int j = temp.length;
107 
108         int startAt = zeroCount;
109         while (startAt < input58.length) {
110             
111             byte mod = divmod256(input58, startAt);
112             if (input58[startAt] == 0) {
113                 ++startAt;
114             }
115 
116             temp[--j] = mod;
117         }
118 
119         // Do no add extra leading zeroes, move j to first non null byte.
120         while (j < temp.length && temp[j] == 0) {
121             ++j;
122         }
123 
124         return copyOfRange(temp, j - zeroCount, temp.length);
125     }
126 
127     private static byte divmod58(byte[] number, int startAt) {
128         
129         var remainder = 0;
130         for (int i = startAt; i < number.length; i++) {
131             
132             int digit256 = number[i] & 0xFF;
133             int temp = remainder * BASE_256 + digit256;
134 
135             number[i] = (byte) (temp / BASE_58);
136 
137             remainder = temp % BASE_58;
138         }
139 
140         return (byte) remainder;
141     }
142 
143     private static byte divmod256(byte[] number58, int startAt) {
144         
145         var remainder = 0;
146         for (int i = startAt; i < number58.length; i++) {
147             
148             int digit58 = number58[i] & 0xFF;
149             int temp = remainder * BASE_58 + digit58;
150 
151             number58[i] = (byte) (temp / BASE_256);
152 
153             remainder = temp % BASE_256;
154         }
155 
156         return (byte) remainder;
157     }
158 
159     private static byte[] copyOfRange(byte[] source, int from, int to) {
160         
161         var range = new byte[to - from];
162         System.arraycopy(source, from, range, 0, range.length);
163 
164         return range;
165     }
166 }