org.icefaces.impl.util.Base64 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icefaces Show documentation
Show all versions of icefaces Show documentation
The core library of the ICEfaces product.
/*
* Copyright 2004-2014 ICEsoft Technologies Canada Corp.
*
* Licensed 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.icefaces.impl.util;
/**
* This Base64
is a utility class for encoding using the Base64
* encoding.
*
* The Base64 encoding is designed to represent arbitrary sequences of octets in
* a form that need to be humanly readable. The encoding and decoding algorithms
* are simple, but the encoded data are consitently only about 33% larger than
* the unencoded data.
*
* For more complete information about the Base64 encoding, please read MIME (Multipurpose
* Internet Mail Extensions) Part One (Section 5.2).
*/
public class Base64 {
private static final byte[] BASE64_ALPHABET_ARRAY = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'+', '/'
};
private static final byte[] BASE64_FOR_URL_ALPHABET_ARRAY = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'-', '_'
};
private static final byte PAD = '=';
/**
* Encodes the specified bytes
using the Base64 encoding.
*
* @param bytes the bytes to be encoded.
* @return the Base64-encoded bytes.
* @see #encode(String)
*/
public static byte[] encode(byte[] bytes) {
return encode(bytes, BASE64_ALPHABET_ARRAY, true);
}
/**
* Encodes the specified string
using the Base64 encoding.
*
* @param string the string to be encoded.
* @return the Base64-encoded string.
* @see #encode(byte[])
*/
public static String encode(String string) {
if (string == null) {
return null;
}
return new String(encode(string.getBytes()));
}
/**
* Encodes the specified bytes
using the Base64 encoding for
* URL usage.
*
* @param bytes the bytes to be encoded.
* @return the Base64-encoded bytes for URL usage.
*/
public static byte[] encodeForURL(byte[] bytes) {
return encode(bytes, BASE64_FOR_URL_ALPHABET_ARRAY, false);
}
private static byte[] encode(
byte[] bytes, byte[] alphabetArray, boolean usePadding) {
if (bytes == null) {
return null;
} else if (bytes.length == 0) {
return bytes;
}
int _length = bytes.length;
int _remainder = _length % 3;
byte[] _bytes;
if (usePadding) {
_bytes = new byte[(_length + 2) / 3 * 4];
} else {
_bytes =
new byte[
((_length + 2) / 3 * 4) -
(_remainder != 0 ? 3 - _remainder : 0)];
}
_length -= _remainder;
int _group;
int _i;
int _index = 0;
for (_i = 0; _i < _length;) {
_group =
(bytes[_i++] & 0xFF) << 16 |
(bytes[_i++] & 0xFF) << 8 |
bytes[_i++] & 0xFF;
_bytes[_index++] = alphabetArray[_group >>> 18];
_bytes[_index++] = alphabetArray[_group >>> 12 & 0x3F];
_bytes[_index++] = alphabetArray[_group >>> 6 & 0x3F];
_bytes[_index++] = alphabetArray[_group & 0x3F];
}
switch (_remainder) {
case 0:
break;
case 1:
_group = (bytes[_i] & 0xFF) << 4;
_bytes[_index++] = alphabetArray[_group >>> 6];
if (usePadding) {
_bytes[_index++] = alphabetArray[_group & 0x3F];
_bytes[_index++] = PAD;
_bytes[_index] = PAD;
} else {
_bytes[_index] = alphabetArray[_group & 0x3F];
}
break;
case 2:
_group = ((bytes[_i++] & 0xFF) << 8 | (bytes[_i] & 0xFF)) << 2;
_bytes[_index++] = alphabetArray[_group >>> 12];
_bytes[_index++] = alphabetArray[_group >>> 6 & 0x3F];
if (usePadding) {
_bytes[_index++] = alphabetArray[_group & 0x3F];
_bytes[_index] = PAD;
} else {
_bytes[_index] = alphabetArray[_group & 0x3F];
}
break;
default:
// this should never happen.
}
return _bytes;
}
//
// Base64 decoding utility, extracted from Apache Commons Codec 1.8
//
private static final int DECODE_SIZE = 3;
private static final int BYTES_PER_ENCODED_BLOCK = 4;
private static final int BITS_PER_ENCODED_BYTE = 6;
private static final int MASK_8BITS = 0xff;
private static final int EOF = -1;
private static final byte[] DECODE_TABLE = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
public static byte[] decode(final String pArray) {
return decode(getBytesUtf8(pArray));
}
public static byte[] decode(final byte[] pArray) {
if (pArray == null || pArray.length == 0) {
return pArray;
}
final Context context = new Context();
decode(pArray, 0, pArray.length, context);
decode(pArray, 0, EOF, context); // Notify decoder of EOF.
final byte[] result = new byte[context.pos];
readResults(result, 0, result.length, context);
return result;
}
private static void decode(final byte[] in, int inPos, final int inAvail, final Context context) {
if (context.eof) {
return;
}
if (inAvail < 0) {
context.eof = true;
}
for (int i = 0; i < inAvail; i++) {
final byte[] buffer = ensureBufferSize(DECODE_SIZE, context);
final byte b = in[inPos++];
if (b == PAD) {
// We're done.
context.eof = true;
break;
} else {
if (b >= 0 && b < DECODE_TABLE.length) {
final int result = DECODE_TABLE[b];
if (result >= 0) {
context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK;
context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;
if (context.modulus == 0) {
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS);
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
}
}
}
}
}
// Two forms of EOF as far as base64 decoder is concerned: actual
// EOF (-1) and first time '=' character is encountered in stream.
// This approach makes the '=' padding characters completely optional.
if (context.eof && context.modulus != 0) {
final byte[] buffer = ensureBufferSize(DECODE_SIZE, context);
// We have some spare bits remaining
// Output all whole multiples of 8 bits and ignore the rest
switch (context.modulus) {
// case 0 : // impossible, as excluded above
case 1 : // 6 bits - ignore entirely
// TODO not currently tested; perhaps it is impossible?
break;
case 2 : // 12 bits = 8 + 4
context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
break;
case 3 : // 18 bits = 8 + 8 + 2
context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
break;
default:
throw new IllegalStateException("Impossible modulus "+context.modulus);
}
}
}
private static byte[] ensureBufferSize(final int size, final Context context){
if ((context.buffer == null) || (context.buffer.length < context.pos + size)){
return resizeBuffer(context);
}
return context.buffer;
}
private static byte[] resizeBuffer(final Context context) {
if (context.buffer == null) {
context.buffer = new byte[8192];
context.pos = 0;
context.readPos = 0;
} else {
final byte[] b = new byte[context.buffer.length * 2];
System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
context.buffer = b;
}
return context.buffer;
}
private static int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) {
if (context.buffer != null) {
final int len = Math.min(available(context), bAvail);
System.arraycopy(context.buffer, context.readPos, b, bPos, len);
context.readPos += len;
if (context.readPos >= context.pos) {
context.buffer = null; // so hasData() will return false, and this method can return -1
}
return len;
}
return context.eof ? EOF : 0;
}
private static int available(final Context context) {
return context.buffer != null ? context.pos - context.readPos : 0;
}
private static byte[] getBytesUtf8(final String string) {
byte[] bytes;
try {
bytes = string.getBytes("UTF-8");
} catch (java.io.UnsupportedEncodingException e) {
bytes = new byte[0];
}
return bytes;
}
private static class Context {
int ibitWorkArea;
long lbitWorkArea;
byte[] buffer;
int pos;
int readPos;
boolean eof;
int currentLinePos;
int modulus;
Context() {}
@SuppressWarnings("boxing") // OK to ignore boxing here
@Override
public String toString() {
return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " +
"modulus=%s, pos=%s, readPos=%s]", this.getClass().getSimpleName(), java.util.Arrays.toString(buffer),
currentLinePos, eof, ibitWorkArea, lbitWorkArea, modulus, pos, readPos);
}
}
}