Update license to GPLv3, fix header comments
[jFCPlib.git] / src / main / java / net / pterodactylus / fcp / FreenetBase64.java
1 /*
2  * jFCPlib - FreenetBase64.java - Copyright © 2015–2016 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.fcp;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22
23 /**
24  * Freenet-specific Base64 implementation.
25  *
26  * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
27  */
28 public class FreenetBase64 {
29
30         private static final char[] FREENET_BASE64_ALPHABET =
31                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-".toCharArray();
32         private static final int[] REVERSE_FREENET_BASE64_ALPHABET = reverse(FREENET_BASE64_ALPHABET);
33
34         private static int[] reverse(char[] alphabet) {
35                 String alphabetString = new String(alphabet);
36                 int[] reversedAlphabet = new int[128];
37                 for (int i = 0; i < 128; i++) {
38                         if (alphabetString.indexOf(i) > -1) {
39                                 reversedAlphabet[i] = alphabetString.indexOf(i);
40                         } else {
41                                 reversedAlphabet[i] = -1;
42                         }
43                 }
44                 return reversedAlphabet;
45         }
46
47         public String encode(byte[] data) {
48                 StringBuilder result = new StringBuilder();
49                 int currentValue = 0;
50                 int index = 0;
51                 while (index < data.length) {
52                         currentValue = (currentValue << 8) | data[index];
53                         if (index % 3 == 2) {
54                                 result.append(FREENET_BASE64_ALPHABET[(currentValue >> 18) & 0x3f]);
55                                 result.append(FREENET_BASE64_ALPHABET[(currentValue >> 12) & 0x3f]);
56                                 result.append(FREENET_BASE64_ALPHABET[(currentValue >> 6) & 0x3f]);
57                                 result.append(FREENET_BASE64_ALPHABET[currentValue & 0x3f]);
58                         }
59                         index++;
60                 }
61                 if (index % 3 == 1) {
62                         result.append(FREENET_BASE64_ALPHABET[(currentValue >> 2) & 0x3f]);
63                         result.append(FREENET_BASE64_ALPHABET[(currentValue << 4) & 0x3f]);
64                         result.append("==");
65                 } else if (index % 3 == 2) {
66                         result.append(FREENET_BASE64_ALPHABET[(currentValue >> 10) & 0x3f]);
67                         result.append(FREENET_BASE64_ALPHABET[(currentValue >> 4) & 0x3f]);
68                         result.append(FREENET_BASE64_ALPHABET[(currentValue << 2) & 0x3f]);
69                         result.append("=");
70                 }
71                 return result.toString();
72         }
73
74         public byte[] decode(String data) {
75                 ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(data.length() * 3 / 4);
76                 try {
77                         int currentValue = 0;
78                         int index = 0;
79                         for (char c : data.toCharArray()) {
80                                 if (c == '=') {
81                                         break;
82                                 }
83                                 currentValue = (currentValue << 6) | REVERSE_FREENET_BASE64_ALPHABET[c];
84                                 if (index % 4 == 3) {
85                                         dataOutput.write((currentValue >> 16) & 0xff);
86                                         dataOutput.write((currentValue >> 8) & 0xff);
87                                         dataOutput.write(currentValue & 0xff);
88                                 }
89                                 index++;
90                         }
91                         if (index % 4 == 2) {
92                                 dataOutput.write((currentValue >> 4) & 0xff);
93                         } else if (index % 4 == 3) {
94                                 dataOutput.write((currentValue >> 10) & 0xff);
95                                 dataOutput.write((currentValue >> 2) & 0xff);
96                         }
97                         return dataOutput.toByteArray();
98                 } finally {
99                         try {
100                                 dataOutput.close();
101                         } catch (IOException e) {
102                                 throw new RuntimeException(e);
103                         }
104                 }
105         }
106
107 }