Add Freenet-specific base64 implementation
authorDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Tue, 14 Jul 2015 18:46:18 +0000 (20:46 +0200)
committerDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Tue, 14 Jul 2015 18:50:51 +0000 (20:50 +0200)
src/main/java/net/pterodactylus/fcp/FreenetBase64.java [new file with mode: 0644]
src/test/java/net/pterodactylus/fcp/FreenetBase64Test.java [new file with mode: 0644]

diff --git a/src/main/java/net/pterodactylus/fcp/FreenetBase64.java b/src/main/java/net/pterodactylus/fcp/FreenetBase64.java
new file mode 100644 (file)
index 0000000..9c70858
--- /dev/null
@@ -0,0 +1,85 @@
+package net.pterodactylus.fcp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Freenet-specific Base64 implementation.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public class FreenetBase64 {
+
+       private static final char[] FREENET_BASE64_ALPHABET =
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-".toCharArray();
+       private static final int[] REVERSE_FREENET_BASE64_ALPHABET = reverse(FREENET_BASE64_ALPHABET);
+
+       private static int[] reverse(char[] alphabet) {
+               String alphabetString = new String(alphabet);
+               int[] reversedAlphabet = new int[128];
+               for (int i = 0; i < 128; i++) {
+                       if (alphabetString.indexOf(i) > -1) {
+                               reversedAlphabet[i] = alphabetString.indexOf(i);
+                       } else {
+                               reversedAlphabet[i] = -1;
+                       }
+               }
+               return reversedAlphabet;
+       }
+
+       public String encode(byte[] data) {
+               StringBuilder result = new StringBuilder();
+               int currentValue = 0;
+               int index = 0;
+               while (index < data.length) {
+                       currentValue = (currentValue << 8) | data[index];
+                       if (index % 3 == 2) {
+                               result.append(FREENET_BASE64_ALPHABET[(currentValue >> 18) & 0b111111]);
+                               result.append(FREENET_BASE64_ALPHABET[(currentValue >> 12) & 0b111111]);
+                               result.append(FREENET_BASE64_ALPHABET[(currentValue >> 6) & 0b111111]);
+                               result.append(FREENET_BASE64_ALPHABET[currentValue & 0b111111]);
+                       }
+                       index++;
+               }
+               if (index % 3 == 1) {
+                       result.append(FREENET_BASE64_ALPHABET[(currentValue >> 2) & 0b111111]);
+                       result.append(FREENET_BASE64_ALPHABET[(currentValue << 4) & 0b111111]);
+                       result.append("==");
+               } else if (index % 3 == 2) {
+                       result.append(FREENET_BASE64_ALPHABET[(currentValue >> 10) & 0b111111]);
+                       result.append(FREENET_BASE64_ALPHABET[(currentValue >> 4) & 0b111111]);
+                       result.append(FREENET_BASE64_ALPHABET[(currentValue << 2) & 0b111111]);
+                       result.append("=");
+               }
+               return result.toString();
+       }
+
+       public byte[] decode(String data) {
+               try (ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(data.length() * 3 / 4)) {
+                       int currentValue = 0;
+                       int index = 0;
+                       for (char c : data.toCharArray()) {
+                               if (c == '=') {
+                                       break;
+                               }
+                               currentValue = (currentValue << 6) | REVERSE_FREENET_BASE64_ALPHABET[c];
+                               if (index % 4 == 3) {
+                                       dataOutput.write((currentValue >> 16) & 0b11111111);
+                                       dataOutput.write((currentValue >> 8) & 0b11111111);
+                                       dataOutput.write(currentValue & 0b11111111);
+                               }
+                               index++;
+                       }
+                       if (index % 4 == 2) {
+                               dataOutput.write((currentValue >> 4) & 0b11111111);
+                       } else if (index % 4 == 3) {
+                               dataOutput.write((currentValue >> 10) & 0b11111111);
+                               dataOutput.write((currentValue >> 2) & 0b11111111);
+                       }
+                       return dataOutput.toByteArray();
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/fcp/FreenetBase64Test.java b/src/test/java/net/pterodactylus/fcp/FreenetBase64Test.java
new file mode 100644 (file)
index 0000000..45838e2
--- /dev/null
@@ -0,0 +1,39 @@
+package net.pterodactylus.fcp;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link FreenetBase64}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FreenetBase64Test {
+
+       private final FreenetBase64 freenetBase64 = new FreenetBase64();
+
+       @Test
+       public void canEncodeString() {
+               assertThat(freenetBase64.encode("".getBytes()), is(""));
+               assertThat(freenetBase64.encode("f".getBytes()), is("Zg=="));
+               assertThat(freenetBase64.encode("fo".getBytes()), is("Zm8="));
+               assertThat(freenetBase64.encode("foo".getBytes()), is("Zm9v"));
+               assertThat(freenetBase64.encode("foob".getBytes()), is("Zm9vYg=="));
+               assertThat(freenetBase64.encode("fooba".getBytes()), is("Zm9vYmE="));
+               assertThat(freenetBase64.encode("foobar".getBytes()), is("Zm9vYmFy"));
+       }
+
+       @Test
+       public void canDecodeStrings() {
+               assertThat(freenetBase64.decode(""), is("".getBytes()));
+               assertThat(freenetBase64.decode("Zg=="), is("f".getBytes()));
+               assertThat(freenetBase64.decode("Zm8="), is("fo".getBytes()));
+               assertThat(freenetBase64.decode("Zm9v"), is("foo".getBytes()));
+               assertThat(freenetBase64.decode("Zm9vYg=="), is("foob".getBytes()));
+               assertThat(freenetBase64.decode("Zm9vYmE="), is("fooba".getBytes()));
+               assertThat(freenetBase64.decode("Zm9vYmFy"), is("foobar".getBytes()));
+       }
+
+}