blob: 2d1950f879ea10fd529fd9489f77fd440e4f8d24 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.vinci.transport.util;
/**
* Provides utility methods for Binary <=> Base64 conversion.
*/
public class Base64Converter {
/**
* If you ask for line-breaks, this is the maximum line length used.
*/
static public final int LINE_LENGTH = 70;
static private final byte[] B64_CODE = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
(byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
(byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y',
(byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm',
(byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0',
(byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) '+', (byte) '/' };
/**
* Utility class not meant to be instantiated.
*/
private Base64Converter() {
}
/**
* @pre from != null
* @param from -
* @return -
*/
static public byte[] convertBinaryToBase64(byte[] from) {
return convertBinaryToBase64(from, from.length, true);
}
/**
* @pre convert_me != null
* @param convert_me -
* @return -
*/
static public String convertStringToBase64String(String convert_me) {
return new String(convertBinaryToBase64(convert_me.getBytes()));
}
/**
* @pre base64 != null
* @param base64 -
* @return -
* @throws Base64FormatException -
*/
static public String convertBase64StringToString(String base64) throws Base64FormatException {
return new String(convertBase64ToBinary(base64.getBytes()));
}
/**
* @pre count ≤ from.length
* @param from -
* @param count -
* @param line_breaks -
* @return -
*/
static public byte[] convertBinaryToBase64(byte[] from, final int count, boolean line_breaks) {
int size = calculateBase64OutputSize(count, line_breaks);
byte[] tmp = new byte[size];
int used = 0;
for (int done = 0; done < count; done += 3) {
b64encodeOctet(tmp, used, from, done, count - done);
used += 4;
}
if (line_breaks) {
byte[] to = new byte[size];
int pos = 0;
for (int breaks = 0; breaks < used; breaks += LINE_LENGTH) {
int length = used - breaks;
if (length > LINE_LENGTH) {
length = LINE_LENGTH;
}
System.arraycopy(tmp, breaks, to, pos, length);
pos += length;
to[pos++] = (byte) '\n';
}
// Debug.Assert(pos == to.length);
return to;
} else {
// Debug.Assert(used == tmp.length);
return tmp;
}
}
/**
* Calculates the size of the resulting Base64 string returned by this class for a binary byte
* array of the specified length. Includes carriage returns and all.
* @param input_size -
* @param line_breaks -
* @return -
*/
static public int calculateBase64OutputSize(int input_size, boolean line_breaks) {
int q = input_size / 3;
if (input_size % 3 != 0) {
q += 1;
}
// Output is always a multiple of 4 characters.
q *= 4;
// Factor in the extra length needed for line breaks.
if (line_breaks) {
q += (q / LINE_LENGTH) + (q % LINE_LENGTH == 0 ? 0 : 1);
}
return q;
}
/**
* @pre input != null
* @param input -
* @return -
* @throws Base64FormatException -
*/
static public byte[] convertBase64ToBinary(byte[] input) throws Base64FormatException {
return convertBase64ToBinary(input, input.length);
}
/**
* @pre input != null
* @pre input_size &le; input.length
* @param input -
* @param input_size -
* @return -
* @throws Base64FormatException -
*/
static public byte[] convertBase64ToBinary(byte[] input, final int input_size)
throws Base64FormatException {
int output_size = calculateBinaryOutputSize(input, input_size);
byte[] output = new byte[output_size];
int pos = 0;
int i = 0;
while (pos + 4 <= input_size) {
pos = b64decodeOctet(input, pos, output, 3 * i, input_size);
i++;
}
return output;
}
/**
* Calculate the number of bytes encoded by a given Base64 input.
*
* @pre input != null
* @pre input_size &le; input.length
* @param input -
* @param input_size -
* @return -
* @throws Base64FormatException -
*/
static public int calculateBinaryOutputSize(byte[] input, final int input_size)
throws Base64FormatException {
int output_size = 0;
for (int i = 0; i + 4 <= input_size;) {
i = consumeInvalidDigits(input, i, input_size);
i++;
i = consumeInvalidDigits(input, i, input_size);
i++;
i = consumeInvalidDigits(input, i, input_size);
if (input[i] == (byte) '=') {
output_size += 1;
break;
}
i++;
i = consumeInvalidDigits(input, i, input_size);
if (input[i] == (byte) '=') {
output_size += 2;
break;
}
i++;
output_size += 3;
}
return output_size;
}
static private int consumeInvalidDigits(byte[] in, int off, int max_offset)
throws Base64FormatException {
if (off >= max_offset) {
throw new Base64FormatException("short read");
}
while (!b64validDigit(in[off])) {
off++;
if (off >= max_offset) {
throw new Base64FormatException("short read");
}
}
return off;
}
/**
* @pre in != null
* @pre out != null
*/
static private int b64decodeOctet(byte[] in, int in_offset, byte[] out, int out_offset,
int max_offset) throws Base64FormatException {
int A;
int B;
int C;
int D;
in_offset = consumeInvalidDigits(in, in_offset, max_offset);
A = in[in_offset++];
if (A < 0) {
throw new Base64FormatException();
}
in_offset = consumeInvalidDigits(in, in_offset, max_offset);
B = in[in_offset++];
if (B < 0) {
throw new Base64FormatException();
}
in_offset = consumeInvalidDigits(in, in_offset, max_offset);
C = in[in_offset++];
in_offset = consumeInvalidDigits(in, in_offset, max_offset);
D = in[in_offset++];
A = b64decodeDigit(A);
A <<= 2;
B = b64decodeDigit(B);
A |= (B >> 4);
out[out_offset] = (byte) A;
B <<= 4;
C = b64decodeDigit(C);
if (C < 0) {
return max_offset; // we are done
}
B |= C >> 2;
out[out_offset + 1] = (byte) B;
C <<= 6;
D = b64decodeDigit(D);
if (D < 0) {
return max_offset; // we are done
}
C |= D;
out[out_offset + 2] = (byte) C;
return in_offset;
}
static private int b64decodeDigit(int c) {
if (c >= (byte) 'A' && c <= (byte) 'Z') {
return c - (byte) 'A';
} else if (c >= (byte) 'a' && c <= (byte) 'z') {
return c - (byte) 'a' + 26;
} else if (c >= (byte) '0' && c <= (byte) '9') {
return c - (byte) '0' + 52;
} else if (c == (byte) '+') {
return 62;
} else if (c == (byte) '/') {
return 63;
} else if (c == (byte) '=') {
return -2;
}
return -1;
}
static private boolean b64validDigit(byte a) {
if (a >= (byte) 'A' && a <= (byte) 'Z') {
return true;
}
if (a >= (byte) 'a' && a <= (byte) 'z') {
return true;
}
if (a >= (byte) '0' && a <= (byte) '9') {
return true;
}
if (a == (byte) '/' || a == (byte) '+' || a == (byte) '=') {
return true;
}
return false;
}
static private void b64encodeOctet(byte[] to, int to_offset, byte[] from, int from_offset,
int count) {
int A = 0;
int B = 0;
int C = 0;
int D = 0;
int tmp = 0;
A = from[from_offset];
if (A < 0) {
A += 256;
}
A >>= 2;
B = from[from_offset];
if (B < 0) {
B += 256;
}
B &= 3;
B <<= 4;
if (count > 1) {
tmp = from[from_offset + 1] < 0 ? (from[from_offset + 1]) + 256 : from[from_offset + 1];
B |= tmp >> 4;
C = from[from_offset + 1];
if (C < 0) {
C += 256;
}
C &= 15;
C <<= 2;
}
if (count > 2) {
tmp = from[from_offset + 2] < 0 ? (from[from_offset + 2]) + 256 : from[from_offset + 2];
C |= tmp >> 6;
D = from[from_offset + 2];
if (D < 0) {
D += 256;
}
D &= 0x3F;
}
to[to_offset] = b64codes(A);
to[to_offset + 1] = b64codes(B);
if (count > 1) {
to[to_offset + 2] = b64codes(C);
} else {
to[to_offset + 2] = (byte) '=';
}
if (count > 2) {
to[to_offset + 3] = b64codes(D);
} else {
to[to_offset + 3] = (byte) '=';
}
}
static private byte b64codes(int which) {
if (which < 0) {
which += 256;
}
return B64_CODE[which];
}
/*
* public static void main(String[] args) throws Base64FormatException { int to = 256; if
* (args.length > 0) { to = Integer.parseInt(args[0]); } System.out.println("TESTING case: " +
* to); byte[] input = new byte[to]; for (int i=0; i<to; i++) { input[i] = (byte)i; } byte[]
* output = convertBinaryToBase64(input); String output_me = new String(output, 0, output.length);
* System.out.println("Encoded string:\n" + output_me); input = convertBase64ToBinary(output); for
* (int i=0; i<to; i++) { if (input[i] &ge; 0) { Debug.Assert(i == (int)input[i]); } else {
* Debug.Assert(i == ((int)input[i]) + 256); } } if (to != 0 && args.length > 0) { // Test all
* cases all the way down to 0 length arrays args[0] = Integer.toString(to-1); main(args); } }
*/
} // class