From 3a59547f237b87ac551efc515884f342cd08b543 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 14 Jul 2015 20:46:18 +0200 Subject: [PATCH] Add Freenet-specific base64 implementation --- .../java/net/pterodactylus/fcp/FreenetBase64.java | 85 ++++++++++++++++++++++ .../net/pterodactylus/fcp/FreenetBase64Test.java | 39 ++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/main/java/net/pterodactylus/fcp/FreenetBase64.java create mode 100644 src/test/java/net/pterodactylus/fcp/FreenetBase64Test.java diff --git a/src/main/java/net/pterodactylus/fcp/FreenetBase64.java b/src/main/java/net/pterodactylus/fcp/FreenetBase64.java new file mode 100644 index 0000000..9c70858 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/FreenetBase64.java @@ -0,0 +1,85 @@ +package net.pterodactylus.fcp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Freenet-specific Base64 implementation. + * + * @author David ‘Bombe’ Roden + */ +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 index 0000000..45838e2 --- /dev/null +++ b/src/test/java/net/pterodactylus/fcp/FreenetBase64Test.java @@ -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 David ‘Bombe’ Roden + */ +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())); + } + +} -- 2.7.4