001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032
033 package com.imagero.uio.io;
034
035 import java.io.ByteArrayInputStream;
036 import java.io.ByteArrayOutputStream;
037 import java.io.IOException;
038 import java.io.InputStream;
039 import java.io.OutputStream;
040 import java.io.Reader;
041 import java.io.Writer;
042
043 /**
044 * This class shows how simple and straightforward can be implementation
045 * of base 64 codec using BitInputStream and BitOutputStream
046 * @author Andrey Kuznetsov
047 */
048 public class Base64 {
049
050 private static final char encodeTable[] = {
051 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
052 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
053 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
054 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
055 };
056
057 static int decodeTable [] = new int[0x100];
058 static int lineLength = 72;
059
060 public static int getLineLength() {
061 return lineLength;
062 }
063
064 public static void setLineLength(int lineLength) {
065 Base64.lineLength = lineLength;
066 }
067
068
069 static {
070 for (int i = 0; i < decodeTable.length; i++) {
071 decodeTable[i] = -1;
072 }
073 for (int j = 0; j < encodeTable.length; j++) {
074 decodeTable[encodeTable[j]] = j;
075 }
076 }
077
078 private static final char [] crlf = "\n".toCharArray();
079
080 /**
081 * base 64 encode
082 * @param b data to encode
083 * @return base 64 encoded String
084 * @throws IOException
085 */
086 public static String base64Encode(byte[] b) throws IOException {
087 BitInputStream bis = new BitInputStream(new ByteArrayInputStream(b));
088 bis.setBitsToRead(6);
089 StringBuffer sb = new StringBuffer();
090 int cnt = 0;
091 while (true) {
092 int c = bis.read();
093 //EOF reached
094 if (c == -1) {
095 break;
096 }
097 sb.append(encodeTable[c]);
098 if(++cnt == lineLength) {
099 sb.append("\n");
100 cnt = 0;
101 }
102 }
103 while ((sb.length() % 4) != 0) {
104 //padding
105 sb.append('=');
106 }
107 return sb.toString();
108 }
109
110 /**
111 * base64 encode data from InputStream and write ict to given character stream
112 * @param in InputStream
113 * @param out Writer
114 * @throws IOException
115 */
116 public static void base64Encode(InputStream in, Writer out) throws IOException {
117 BitInputStream bis = new BitInputStream(in);
118 char[] buffer = new char[4000];
119 bis.setBitsToRead(6);
120 int cnt = 0;
121 int bufferPtr = 0;
122 while (true) {
123 int c = bis.read();
124 //EOF reached
125 if (c == -1) {
126 break;
127 }
128 buffer[bufferPtr++] = encodeTable[c];
129 if(++cnt == lineLength) {
130 if(bufferPtr + crlf.length >= buffer.length) {
131 out.write(buffer, 0, bufferPtr);
132 bufferPtr = 0;
133 }
134 for (int i = 0; i < crlf.length; i++) {
135 buffer[bufferPtr++] = crlf[i];
136 }
137 cnt = 0;
138 }
139 if (bufferPtr == buffer.length) {
140 out.write(buffer);
141 bufferPtr = 0;
142 }
143 }
144 while ((bufferPtr % 4) != 0) {
145 //padding
146 buffer[bufferPtr++] ='=';
147 }
148 if (bufferPtr > 0) {
149 out.write(buffer, 0, bufferPtr);
150 }
151 }
152
153 /**
154 * decode base 64 encoded string
155 * @param s String to decode
156 * @return byte array
157 * @throws IOException
158 */
159 public static byte[] base64Decode(String s) throws IOException {
160 char[] chrs = s.toCharArray();
161 ByteArrayOutputStream bout = new ByteArrayOutputStream();
162 BitOutputStream bos = new BitOutputStream(bout);
163 bos.setBitsToWrite(6);
164
165 boolean flush = true;
166
167 for (int i = 0; i < chrs.length; i++) {
168 char c = chrs[i];
169 //start of padding
170 if (c == '=') {
171 flush = false;
172 break;
173 }
174 int c0 = decodeTable[c];
175 if(c0 != -1) {
176 bos.write(c0);
177 }
178 }
179 if (flush) {
180 bos.flush();
181 }
182 return bout.toByteArray();
183 }
184
185
186 /**
187 * decode base64 encoded character stream and write it to given OutputStream
188 * @param in Reader character stream
189 * @param out OutputStream
190 * @throws IOException
191 */
192 public static void base64Decode(Reader in, OutputStream out) throws IOException {
193 BitOutputStream bos = new BitOutputStream(out);
194 bos.setBitsToWrite(6);
195
196 boolean flush = true;
197
198 while (true) {
199 int c = in.read();
200 if (c == -1) {
201 break;
202 }
203 //start of padding
204 if (c == '=') {
205 flush = false;
206 break;
207 }
208 final int b = decodeTable[c];
209 if(b != -1) {
210 bos.write(b);
211 }
212 }
213 if (flush) {
214 bos.flush();
215 }
216 }
217 }