/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.io;
import com.liferay.portal.kernel.util.CharPool;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Tina Tian
*/
public class Base64InputStream extends InputStream {
public Base64InputStream(InputStream inputStream) {
_inputStream = inputStream;
_unitBufferIndex = 0;
_avaiableBytes = 0;
_unitBuffer = new byte[3];
}
@Override
public int available() throws IOException {
return ((_inputStream.available() * 3) / 4) + _avaiableBytes;
}
@Override
public int read() throws IOException {
if (_avaiableBytes == 0) {
_avaiableBytes = decodeUnit(_unitBuffer, 0);
if (_avaiableBytes <= 0) {
return -1;
}
_unitBufferIndex = 0;
}
_avaiableBytes--;
return _unitBuffer[_unitBufferIndex++] & 0xff;
}
@Override
public int read(byte[] bytes, int offset, int length) throws IOException {
if ((length <= 0) || (offset < 0)) {
return -1;
}
int initialLength = length;
while ((_avaiableBytes > 0) && (length > 0)) {
bytes[offset++] = _unitBuffer[_unitBufferIndex++];
_avaiableBytes--;
length--;
}
int bytesLength = length - (length % 3);
while (bytesLength > 0) {
int returnValue = decodeUnit(bytes, offset);
if (returnValue > 0) {
offset += returnValue;
length -= returnValue;
}
if (returnValue < 3) {
if (initialLength == length) {
return -1;
}
return initialLength - length;
}
bytesLength -= 3;
}
while (length > 0) {
int intValue = read();
if (intValue == -1) {
break;
}
bytes[offset++] = (byte)intValue;
length--;
}
if (initialLength == length) {
return -1;
}
return initialLength - length;
}
@Override
public long skip(long skip) throws IOException {
long initialSkip = skip;
while (skip > 0) {
if (read() <= 0) {
break;
}
skip--;
}
return initialSkip - skip;
}
protected int decode(
byte[] bytes, byte[] outputBuffer, int position, int padNumber) {
int intValue = 0;
for (int next = 0; next < 4; next++) {
intValue <<= 6;
intValue |= bytes[next];
}
if (padNumber == 2) {
intValue >>= 16;
outputBuffer[position] = (byte)(intValue & 0xff);
return 1;
}
else if (padNumber == 1) {
intValue >>= 8;
outputBuffer[position + 1] = (byte)(intValue & 0xff);
intValue >>= 8;
outputBuffer[position] = (byte)(intValue & 0xff);
return 2;
}
else if (padNumber == 0) {
outputBuffer[position + 2] = (byte)(intValue & 0xff);
intValue >>= 8;
outputBuffer[position + 1] = (byte)(intValue & 0xff);
intValue >>= 8;
outputBuffer[position] = (byte)(intValue & 0xff);
return 3;
}
else {
return -1;
}
}
protected int decodeUnit(byte[] outputBuffer, int position)
throws IOException {
int intValue = -1;
int padNumber = 0;
int count = 0;
byte[] decodeUnitBuffer = new byte[4];
while (count < 4) {
intValue = getEncodedByte();
if (intValue == -1) {
return -1;
}
if (intValue == -2) {
if (count < 2) {
return -1;
}
padNumber++;
count++;
while (count < 4) {
intValue = getEncodedByte();
if (intValue != -2) {
return -1;
}
padNumber++;
count++;
}
int returnValue = decode(
decodeUnitBuffer, outputBuffer, position, padNumber);
return returnValue;
}
decodeUnitBuffer[count++] = (byte)intValue;
}
return decode(decodeUnitBuffer, outputBuffer, position, padNumber);
}
protected int getByte(char character) {
if ((character >= CharPool.UPPER_CASE_A) &&
(character <= CharPool.UPPER_CASE_Z)) {
return character - 65;
}
if ((character >= CharPool.LOWER_CASE_A) &&
(character <= CharPool.LOWER_CASE_Z)) {
return (character - 97) + 26;
}
if ((character >= CharPool.NUMBER_0) &&
(character <= CharPool.NUMBER_9)) {
return (character - 48) + 52;
}
if (character == CharPool.PLUS) {
return 62;
}
if (character == CharPool.SLASH) {
return 63;
}
if (character != CharPool.EQUAL) {
return -1;
}
else {
return 0;
}
}
protected int getEncodedByte() throws IOException {
while (true) {
int returnValue = _inputStream.read();
if (returnValue == -1) {
return -1;
}
char character = (char)(returnValue & 0xff);
if (character == CharPool.EQUAL) {
return -2;
}
int byteValue = getByte(character);
if (byteValue == -1) {
continue;
}
return byteValue;
}
}
private int _avaiableBytes;
private final InputStream _inputStream;
private final byte[] _unitBuffer;
private int _unitBufferIndex;
}