4c3422d060904046c1872871464c6161585c234d
[jFCPlib.git] / src / main / java / net / pterodactylus / fcp / FreenetBase64.java
1 package net.pterodactylus.fcp;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5
6 /**
7  * Freenet-specific Base64 implementation.
8  *
9  * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
10  */
11 public class FreenetBase64 {
12
13         private static final char[] FREENET_BASE64_ALPHABET =
14                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-".toCharArray();
15         private static final int[] REVERSE_FREENET_BASE64_ALPHABET = reverse(FREENET_BASE64_ALPHABET);
16
17         private static int[] reverse(char[] alphabet) {
18                 String alphabetString = new String(alphabet);
19                 int[] reversedAlphabet = new int[128];
20                 for (int i = 0; i < 128; i++) {
21                         if (alphabetString.indexOf(i) > -1) {
22                                 reversedAlphabet[i] = alphabetString.indexOf(i);
23                         } else {
24                                 reversedAlphabet[i] = -1;
25                         }
26                 }
27                 return reversedAlphabet;
28         }
29
30         public String encode(byte[] data) {
31                 StringBuilder result = new StringBuilder();
32                 int currentValue = 0;
33                 int index = 0;
34                 while (index < data.length) {
35                         currentValue = (currentValue << 8) | data[index];
36                         if (index % 3 == 2) {
37                                 result.append(FREENET_BASE64_ALPHABET[(currentValue >> 18) & 0x3f]);
38                                 result.append(FREENET_BASE64_ALPHABET[(currentValue >> 12) & 0x3f]);
39                                 result.append(FREENET_BASE64_ALPHABET[(currentValue >> 6) & 0x3f]);
40                                 result.append(FREENET_BASE64_ALPHABET[currentValue & 0x3f]);
41                         }
42                         index++;
43                 }
44                 if (index % 3 == 1) {
45                         result.append(FREENET_BASE64_ALPHABET[(currentValue >> 2) & 0x3f]);
46                         result.append(FREENET_BASE64_ALPHABET[(currentValue << 4) & 0x3f]);
47                         result.append("==");
48                 } else if (index % 3 == 2) {
49                         result.append(FREENET_BASE64_ALPHABET[(currentValue >> 10) & 0x3f]);
50                         result.append(FREENET_BASE64_ALPHABET[(currentValue >> 4) & 0x3f]);
51                         result.append(FREENET_BASE64_ALPHABET[(currentValue << 2) & 0x3f]);
52                         result.append("=");
53                 }
54                 return result.toString();
55         }
56
57         public byte[] decode(String data) {
58                 ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(data.length() * 3 / 4);
59                 try {
60                         int currentValue = 0;
61                         int index = 0;
62                         for (char c : data.toCharArray()) {
63                                 if (c == '=') {
64                                         break;
65                                 }
66                                 currentValue = (currentValue << 6) | REVERSE_FREENET_BASE64_ALPHABET[c];
67                                 if (index % 4 == 3) {
68                                         dataOutput.write((currentValue >> 16) & 0xff);
69                                         dataOutput.write((currentValue >> 8) & 0xff);
70                                         dataOutput.write(currentValue & 0xff);
71                                 }
72                                 index++;
73                         }
74                         if (index % 4 == 2) {
75                                 dataOutput.write((currentValue >> 4) & 0xff);
76                         } else if (index % 4 == 3) {
77                                 dataOutput.write((currentValue >> 10) & 0xff);
78                                 dataOutput.write((currentValue >> 2) & 0xff);
79                         }
80                         return dataOutput.toByteArray();
81                 } finally {
82                         try {
83                                 dataOutput.close();
84                         } catch (IOException e) {
85                                 throw new RuntimeException(e);
86                         }
87                 }
88         }
89
90 }