Skip to content

Commit 2392982

Browse files
authored
Merge pull request #95 from filip26/fix/issue-94
v1.2.0
2 parents 4a422cd + 5cf4df4 commit 2392982

8 files changed

Lines changed: 274 additions & 106 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ var decoder = MultibaseDecorer.getInstance(mybase, ...);
7474
<dependency>
7575
<groupId>com.apicatalog</groupId>
7676
<artifactId>copper-multibase</artifactId>
77-
<version>1.1.0</version>
77+
<version>1.2.0</version>
7878
</dependency>
7979
```
8080

pom.xml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<modelVersion>4.0.0</modelVersion>
88
<groupId>com.apicatalog</groupId>
99
<artifactId>copper-multibase</artifactId>
10-
<version>1.1.0</version>
10+
<version>1.2.0</version>
1111
<packaging>jar</packaging>
1212

1313
<name>Copper Multibase</name>
@@ -163,15 +163,15 @@
163163
</execution>
164164
</executions>
165165
</plugin>
166-
<plugin>
167-
<groupId>org.sonatype.central</groupId>
168-
<artifactId>central-publishing-maven-plugin</artifactId>
169-
<version>0.8.0</version>
170-
<extensions>true</extensions>
171-
<configuration>
172-
<publishingServerId>central</publishingServerId>
173-
<autoPublish>true</autoPublish>
174-
</configuration>
166+
<plugin>
167+
<groupId>org.sonatype.central</groupId>
168+
<artifactId>central-publishing-maven-plugin</artifactId>
169+
<version>0.8.0</version>
170+
<extensions>true</extensions>
171+
<configuration>
172+
<publishingServerId>central</publishingServerId>
173+
<autoPublish>true</autoPublish>
174+
</configuration>
175175
</plugin>
176176
</plugins>
177177
</build>

src/main/java/com/apicatalog/base/Base16.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,28 @@
55
*/
66
public class Base16 {
77

8-
public static char[] ALPHABET_LOWER = new char[] {
8+
public static final char[] ALPHABET_LOWER = new char[] {
99
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1010
'a', 'b', 'c', 'd', 'e', 'f',
1111
};
1212

13-
public static char[] ALPHABET_UPPER = new char[] {
13+
public static final char[] ALPHABET_UPPER = new char[] {
1414
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1515
'A', 'B', 'C', 'D', 'E', 'F',
1616
};
1717

18+
/**
19+
* Encodes the given byte array into a hexadecimal string using the specified
20+
* alphabet.
21+
*
22+
* @param data the byte array to encode
23+
* @param alphabet the 16-character alphabet (lowercase or uppercase)
24+
* @return the base16-encoded string
25+
* @throws IllegalArgumentException if {@code data} is {@code null}
26+
*/
1827
public static String encode(final byte[] data, final char[] alphabet) {
1928
if (data == null) {
20-
throw new IllegalArgumentException();
29+
throw new IllegalArgumentException("Input data must not be null.");
2130
}
2231
if (data.length == 0) {
2332
return "";
@@ -33,18 +42,26 @@ public static String encode(final byte[] data, final char[] alphabet) {
3342
return encoded.toString();
3443
}
3544

45+
/**
46+
* Decodes a base16-encoded string into a byte array.
47+
*
48+
* @param encoded the base16-encoded string
49+
* @return the decoded byte array
50+
* @throws IllegalArgumentException if input is {@code null}, has odd length, or
51+
* contains non-hex characters
52+
*/
3653
public static byte[] decode(final String encoded) {
3754
if (encoded == null) {
38-
throw new IllegalArgumentException();
55+
throw new IllegalArgumentException("Encoded string must not be null.");
3956
}
4057
if (encoded.isEmpty()) {
4158
return new byte[0];
4259
}
4360

4461
final char[] chars = encoded.toCharArray();
4562

46-
if (chars.length % 2 > 0) {
47-
throw new IllegalArgumentException();
63+
if (chars.length % 2 != 0) {
64+
throw new IllegalArgumentException("Encoded string must have an even number of characters.");
4865
}
4966

5067
final byte[] data = new byte[chars.length / 2];
@@ -60,6 +77,13 @@ public static byte[] decode(final String encoded) {
6077
return data;
6178
}
6279

80+
/**
81+
* Converts a hexadecimal character to its integer value.
82+
*
83+
* @param ch the character to convert
84+
* @return integer value of the hex character
85+
* @throws IllegalArgumentException if the character is not in [0-9a-fA-F]
86+
*/
6387
public static int charToCode(char ch) {
6488
if (ch >= 'a' && ch <= 'f') {
6589
return ch - (int) 'a' + 10;
@@ -70,6 +94,6 @@ public static int charToCode(char ch) {
7094
if (ch >= '0' && ch <= '9') {
7195
return ch - (int) '0';
7296
}
73-
throw new IllegalArgumentException("Illegal character '" + ch + "'");
97+
throw new IllegalArgumentException("Invalid hexadecimal character: '" + ch + "'");
7498
}
7599
}

src/main/java/com/apicatalog/base/Base2.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
package com.apicatalog.base;
22

3+
/**
4+
* Base2 encoding and decoding (binary).
5+
* <p>
6+
* Each byte is represented by 8 bits (characters '0' or '1').
7+
*/
38
public class Base2 {
49

510
private Base2() {
611
/* protected */}
712

13+
/**
14+
* Encodes the given byte array into a Base2 (binary) string.
15+
*
16+
* @param data the byte array to encode
17+
* @return a binary string representing the data
18+
* @throws IllegalArgumentException if {@code data} is {@code null}
19+
*/
820
public static String encode(final byte[] data) {
921
if (data == null) {
10-
throw new IllegalArgumentException();
22+
throw new IllegalArgumentException("Input data must not be null.");
1123
}
1224
if (data.length == 0) {
1325
return "";
@@ -28,18 +40,26 @@ public static String encode(final byte[] data) {
2840
return encoded.toString();
2941
}
3042

43+
/**
44+
* Decodes a Base2-encoded string into a byte array.
45+
*
46+
* @param encoded the binary string to decode
47+
* @return the decoded byte array
48+
* @throws IllegalArgumentException if {@code encoded} is {@code null},
49+
* has invalid length (not a multiple of 8), or contains characters other than '0' or '1'
50+
*/
3151
public static byte[] decode(final String encoded) {
3252
if (encoded == null) {
33-
throw new IllegalArgumentException();
53+
throw new IllegalArgumentException("Encoded string must not be null.");
3454
}
3555
if (encoded.isEmpty()) {
3656
return new byte[0];
3757
}
3858

3959
final char[] chars = encoded.toCharArray();
4060

41-
if (chars.length % 8 > 0) {
42-
throw new IllegalArgumentException();
61+
if (chars.length % 8 != 0) {
62+
throw new IllegalArgumentException("Encoded string length must be a multiple of 8.");
4363
}
4464

4565
final byte[] data = new byte[chars.length / 8];
@@ -69,6 +89,6 @@ static int charToCode(char ch) {
6989
if (ch == '1') {
7090
return 1;
7191
}
72-
throw new IllegalArgumentException("Illegal character '" + ch + "'");
92+
throw new IllegalArgumentException("Invalid character '" + ch + "'. Expected '0' or '1'.");
7393
}
7494
}

src/main/java/com/apicatalog/base/Base32.java

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,69 @@
22

33
import java.util.function.Function;
44

5-
/*
6-
* https://datatracker.ietf.org/doc/html/rfc4648
5+
/**
6+
* Base32 encoder and decoder based on RFC 4648.
7+
* <p>
8+
* Supports both standard Base32 and Base32hex variants, with optional padding.
9+
* </p>
10+
*
11+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4648">RFC 4648</a>
712
*/
813
public class Base32 {
914

10-
public static char[] ALPHABET_UPPER = new char[] {
15+
/** RFC 4648 standard Base32 alphabet (uppercase). */
16+
public static final char[] ALPHABET_UPPER = new char[] {
1117
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
1218
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1319
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2',
1420
'3', '4', '5', '6', '7',
1521
};
1622

17-
public static char[] ALPHABET_LOWER = new char[] {
23+
/** RFC 4648 standard Base32 alphabet (lowercase). */
24+
public static final char[] ALPHABET_LOWER = new char[] {
1825
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
1926
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
2027
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '2',
2128
'3', '4', '5', '6', '7',
2229
};
2330

24-
public static char[] ALPHABET_HEX_LOWER = new char[] {
31+
/** RFC 4648 Base32hex alphabet (lowercase). */
32+
public static final char[] ALPHABET_HEX_LOWER = new char[] {
2533
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
2634
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
2735
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
2836
'u', 'v',
2937
};
3038

31-
public static char[] ALPHABET_HEX_UPPER = new char[] {
39+
/** RFC 4648 Base32hex alphabet (uppercase). */
40+
public static final char[] ALPHABET_HEX_UPPER = new char[] {
3241
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
3342
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
3443
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
3544
'U', 'V',
3645
};
3746

38-
static int[] PADDING = new int[] {
47+
static final int[] PADDING = new int[] {
3948
6, 4, 3, 1, 0,
4049
};
4150

42-
static int[] PADDING_REVERSE = new int[] {
51+
static final int[] PADDING_REVERSE = new int[] {
4352
0, 4, -1, 3, 2, -1, 1, -1,
4453
};
4554

55+
/**
56+
* Encodes the given byte array into a Base32 string using the specified
57+
* alphabet.
58+
*
59+
* @param data the data to encode
60+
* @param alphabet the encoding alphabet (must be 32 characters)
61+
* @param padding whether to include padding characters ('=') in the output
62+
* @return the encoded string
63+
* @throws IllegalArgumentException if {@code data} is null
64+
*/
4665
public static String encode(final byte[] data, final char[] alphabet, final boolean padding) {
4766
if (data == null) {
48-
throw new IllegalArgumentException();
67+
throw new IllegalArgumentException("Input data must not be null.");
4968
}
5069
if (data.length == 0) {
5170
return "";
@@ -101,9 +120,19 @@ public static String encode(final byte[] data, final char[] alphabet, final bool
101120
return encoded.toString();
102121
}
103122

123+
/**
124+
* Decodes a Base32 string into a byte array.
125+
*
126+
* @param encoded the encoded string
127+
* @param charToCode a function that maps each character to its 5-bit code
128+
* @param padding whether padding is expected in the input
129+
* @return the decoded byte array
130+
* @throws IllegalArgumentException if the input is null, malformed, or contains
131+
* invalid characters
132+
*/
104133
public static byte[] decode(final String encoded, final Function<Character, Integer> charToCode, boolean padding) {
105134
if (encoded == null) {
106-
throw new IllegalArgumentException();
135+
throw new IllegalArgumentException("Encoded string must not be null.");
107136
}
108137
if (encoded.isEmpty()) {
109138
return new byte[0];
@@ -118,7 +147,7 @@ public static byte[] decode(final String encoded, final Function<Character, Inte
118147

119148
if (padding) {
120149
if (trailing > 0) {
121-
throw new IllegalArgumentException();
150+
throw new IllegalArgumentException("Invalid padding: base32 string must be a multiple of 8 characters.");
122151
}
123152
pads = getPaddingLength(chars);
124153
length = chars.length - pads;
@@ -185,7 +214,7 @@ public static byte[] decode(final String encoded, final Function<Character, Inte
185214
}
186215

187216
if (rest > 0) {
188-
throw new IllegalArgumentException();
217+
throw new IllegalArgumentException("Invalid base32 string: leftover bits after decoding.");
189218
}
190219

191220
return data;
@@ -198,7 +227,7 @@ static final int getDecodedLength(final int length, int pads) {
198227
final int diff = PADDING_REVERSE[pads];
199228

200229
if (diff == -1) {
201-
throw new IllegalArgumentException("Unknown pad size '" + pads + "'");
230+
throw new IllegalArgumentException("Invalid number of padding characters: " + pads);
202231
}
203232

204233
return total + diff;
@@ -218,6 +247,14 @@ static final int getPaddingLength(final char[] data) {
218247
return pads;
219248
}
220249

250+
/**
251+
* Converts a Base32 character to its 5-bit code. Accepts both upper and
252+
* lowercase letters and digits '2' to '7'.
253+
*
254+
* @param ch the Base32 character
255+
* @return the 5-bit value
256+
* @throws IllegalArgumentException if the character is invalid
257+
*/
221258
public static int charToCode(char ch) {
222259
if (ch >= 'a' && ch <= 'z') {
223260
return ch - (int) 'a';
@@ -228,9 +265,17 @@ public static int charToCode(char ch) {
228265
if (ch >= '2' && ch <= '7') {
229266
return ch - (int) '2' + 26;
230267
}
231-
throw new IllegalArgumentException("Illegal character '" + ch + "'");
268+
throw new IllegalArgumentException("Invalid base32 character: '" + ch + "'");
232269
}
233270

271+
/**
272+
* Converts a Base32hex character to its 5-bit code. Accepts lowercase letters
273+
* 'a' to 'v', uppercase 'A' to 'V', and digits '0' to '9'.
274+
*
275+
* @param ch the Base32hex character
276+
* @return the 5-bit value
277+
* @throws IllegalArgumentException if the character is invalid
278+
*/
234279
public static int charToCodeHex(char ch) {
235280
if (ch >= 'a' && ch <= 'v') {
236281
return ch - (int) 'a' + 10;
@@ -241,6 +286,6 @@ public static int charToCodeHex(char ch) {
241286
if (ch >= '0' && ch <= '9') {
242287
return ch - (int) '0';
243288
}
244-
throw new IllegalArgumentException("Illegal character '" + ch + "'");
289+
throw new IllegalArgumentException("Invalid base32hex character: '" + ch + "'");
245290
}
246291
}

0 commit comments

Comments
 (0)