com.dyuproject.protostuff.B64Code Maven / Gradle / Ivy
//========================================================================
//Copyright 2007-2010 David Yu [email protected]
//------------------------------------------------------------------------
//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 com.dyuproject.protostuff;
//========================================================================
//Copyright 1999-2005 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//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.
//========================================================================
import java.io.IOException;
import java.io.OutputStream;
/* ------------- Ported from org.mortbay.jetty.security.B64Code ------------- */
/** Fast B64 Encoder/Decoder as described in RFC 1421.
* Does not insert or interpret whitespace as described in RFC
* 1521. If you require this you must pre/post process your data.
*
Note that in a web context the usual case is to not want
* linebreaks or other white space in the encoded output.
*
* All methods that begin with 'c' will use char arrays (as output on encode,
* as input on decode).
*
* @author Brett Sealey (bretts)
* @author Greg Wilkins (gregw)
* @author David Yu (dyu)
*/
public final class B64Code
{
// ------------------------------------------------------------------
public static final byte pad = (byte)'=', padUrlSafe = (byte)'=';
public static final byte[] nibble2code=
{
'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','+','/'
};
public static final byte[] nibble2codeUrlSafe=
{
'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','-','_'
};
public static final byte[] code2nibble = new byte[256],
code2nibbleUrlSafe = new byte[256];
static
{
//code2nibble=new byte[256];
//code2nibbleUrlSafe=new byte[256];
for (int i=0;i<256;i++)
{
code2nibble[i]=-1;
code2nibbleUrlSafe[i]=-1;
}
for (byte b=0;b<64;b++)
{
code2nibble[nibble2code[b]]=b;
code2nibbleUrlSafe[nibble2codeUrlSafe[b]]=b;
}
code2nibble[pad]=0;
code2nibbleUrlSafe[padUrlSafe]=0;
}
private B64Code() {}
/**
* Fast Base 64 encode as described in RFC 1421.
*/
public static byte[] encode(byte[] input)
{
return encode(input, 0, input.length);
}
/**
* Fast Base 64 encode as described in RFC 1421.
*/
public static byte[] encode(byte[] input, int inOffset, int inLen)
{
final byte[] output = new byte[((inLen+2)/3)*4];
encode(input, inOffset, inLen, output , 0, nibble2code, pad);
return output;
}
/**
* Fast Base 64 encode as described in RFC 1421.
*/
public static byte[] encodeUS(byte[] input, int inOffset, int inLen)
{
final byte[] output = new byte[((inLen+2)/3)*4];
encode(input, inOffset, inLen, output , 0, nibble2codeUrlSafe, padUrlSafe);
return output;
}
/**
* Fast Base 64 encode as described in RFC 1421.
*/
public static char[] cencode(byte[] input)
{
return cencode(input, 0, input.length);
}
/**
* Fast Base 64 encode as described in RFC 1421.
*/
public static char[] cencode(byte[] input, int inOffset, int inLen)
{
final char[] output = new char[((inLen+2)/3)*4];
cencode(input, inOffset, inLen, output , 0, nibble2code, pad);
return output;
}
/**
* Fast Base 64 encode as described in RFC 1421.
*/
public static char[] cencodeUS(byte[] input, int inOffset, int inLen)
{
final char[] output = new char[((inLen+2)/3)*4];
cencode(input, inOffset, inLen, output , 0, nibble2codeUrlSafe, padUrlSafe);
return output;
}
// ------------------------------------------------------------------
/**
* Fast Base 64 encode as described in RFC 1421.
*
Does not insert whitespace as described in RFC 1521.
*
Avoids creating extra copies of the input/output.
*/
private static void encode(final byte[] input, int inOffset, final int inLen,
final byte[] output, int outOffset,
final byte[] nibble2code, byte pad)
{
byte b0, b1, b2;
final int remaining = inLen%3, stop = inOffset + (inLen-remaining);
while (inOffset < stop)
{
b0=input[inOffset++];
b1=input[inOffset++];
b2=input[inOffset++];
output[outOffset++]=nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
output[outOffset++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
output[outOffset++]=nibble2code[b2&077];
}
switch(remaining)
{
case 0:
break;
case 1:
b0=input[inOffset++];
output[outOffset++]=nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=nibble2code[(b0<<4)&0x3f];
output[outOffset++]=pad;
output[outOffset++]=pad;
break;
case 2:
b0=input[inOffset++];
b1=input[inOffset++];
output[outOffset++]=nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
output[outOffset++]=nibble2code[(b1<<2)&0x3f];
output[outOffset++]=pad;
break;
default:
throw new IllegalStateException("should not happen");
}
}
// ------------------------------------------------------------------
/**
* Fast Base 64 encode as described in RFC 1421.
*
Does not insert whitespace as described in RFC 1521.
*
Avoids creating extra copies of the input/output.
*/
private static void cencode(final byte[] input, int inOffset, final int inLen,
final char[] output, int outOffset,
byte[] nibble2code, byte pad)
{
byte b0, b1, b2;
final int remaining = inLen%3, stop = inOffset + (inLen-remaining);
while (inOffset < stop)
{
b0=input[inOffset++];
b1=input[inOffset++];
b2=input[inOffset++];
output[outOffset++]=(char)nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=(char)nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
output[outOffset++]=(char)nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
output[outOffset++]=(char)nibble2code[b2&077];
}
switch(remaining)
{
case 0:
break;
case 1:
b0=input[inOffset++];
output[outOffset++]=(char)nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=(char)nibble2code[(b0<<4)&0x3f];
output[outOffset++]=(char)pad;
output[outOffset++]=(char)pad;
break;
case 2:
b0=input[inOffset++];
b1=input[inOffset++];
output[outOffset++]=(char)nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=(char)nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
output[outOffset++]=(char)nibble2code[(b1<<2)&0x3f];
output[outOffset++]=(char)pad;
break;
default:
throw new IllegalStateException("should not happen");
}
}
/*private static int encodeExplicit(final byte[] input, int inOffset, final int inLen,
final byte[] output, int outOffset, int loops)
{
for (byte b0, b1, b2; loops-->0;)
{
b0=input[inOffset++];
b1=input[inOffset++];
b2=input[inOffset++];
output[outOffset++]=nibble2code[(b0>>>2)&0x3f];
output[outOffset++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
output[outOffset++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
output[outOffset++]=nibble2code[b2&077];
}
return inOffset;
}*/
/**
* Encodes the byte array into the {@link LinkedBuffer} and grows when full.
*/
public static LinkedBuffer encode(final byte[] input, int inOffset, int inLen,
final WriteSession session, final LinkedBuffer lb) throws IOException
{
return encode(input, inOffset, inLen, nibble2code, pad, session, lb);
}
/**
* Encodes the byte array into the {@link LinkedBuffer} and grows when full.
*/
public static LinkedBuffer encode(final byte[] input, int inOffset, int inLen,
byte[] nibble2code, byte pad,
final WriteSession session, final LinkedBuffer lb) throws IOException
{
int outputSize = ((inLen+2)/3)*4;
session.size += outputSize;
final int available = lb.buffer.length - lb.offset;
if(outputSize > available)
{
int chunks = available/4;
if(chunks == 0)
{
// available size is less than 4
if(outputSize > session.nextBufferSize)
{
final byte[] encoded = new byte[outputSize];
encode(input, inOffset, inLen, encoded, 0, nibble2code, pad);
// return a fresh buffer.
return new LinkedBuffer(session.nextBufferSize,
new LinkedBuffer(encoded, 0, outputSize, lb));
}
final byte[] encoded = new byte[session.nextBufferSize];
encode(input, inOffset, inLen, encoded, 0, nibble2code, pad);
// continue with the existing byte array of the previous buffer
return new LinkedBuffer(encoded, 0, outputSize, lb);
}
int inBefore = inOffset;
final byte[] buffer = lb.buffer;
int offset = lb.offset;
byte b0, b1, b2;
// process available
while(chunks-->0)
{
b0=input[inOffset++];
b1=input[inOffset++];
b2=input[inOffset++];
buffer[offset++]=nibble2code[(b0>>>2)&0x3f];
buffer[offset++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
buffer[offset++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
buffer[offset++]=nibble2code[b2&077];
}
inLen -= (inOffset - inBefore);
outputSize -= (offset - lb.offset);
lb.offset = offset;
if(outputSize > session.nextBufferSize)
{
final byte[] encoded = new byte[outputSize];
encode(input, inOffset, inLen, encoded, 0, nibble2code, pad);
// return a fresh buffer.
return new LinkedBuffer(session.nextBufferSize,
new LinkedBuffer(encoded, 0, outputSize, lb));
}
final byte[] encoded = new byte[session.nextBufferSize];
encode(input, inOffset, inLen, encoded, 0, nibble2code, pad);
// continue with the existing byte array of the previous buffer
return new LinkedBuffer(encoded, 0, outputSize, lb);
}
encode(input, inOffset, inLen, lb.buffer, lb.offset, nibble2code, pad);
lb.offset += outputSize;
return lb;
}
/**
* Encodes the byte array into the {@link LinkedBuffer} and flushes
* to the {@link OutputStream} when buffer is full.
*/
public static LinkedBuffer sencode(final byte[] input, int inOffset, int inLen,
final WriteSession session,
final LinkedBuffer lb) throws IOException
{
return sencode(input, inOffset, inLen, nibble2code, pad, session, lb);
}
/**
* Encodes the byte array into the {@link LinkedBuffer} and flushes
* to the {@link OutputStream} when buffer is full.
*/
public static LinkedBuffer sencode(final byte[] input, int inOffset, int inLen,
byte[] nibble2code, byte pad,
final WriteSession session,
final LinkedBuffer lb) throws IOException
{
int outputSize = ((inLen+2)/3)*4;
session.size += outputSize;
int available = lb.buffer.length - lb.offset;
if(outputSize > available)
{
final byte[] buffer = lb.buffer;
int offset = lb.offset, remaining = inLen%3, chunks = available/4;
byte b0, b1, b2;
for(int stop = inOffset + (inLen-remaining); inOffset < stop; chunks--)
{
if(chunks == 0)
{
// available size is less than 4
// flush and reset
offset = session.flush(buffer, lb.start, offset-lb.start);
//available = buffer.length - offset;
chunks = (buffer.length - offset)/4;
}
b0=input[inOffset++];
b1=input[inOffset++];
b2=input[inOffset++];
buffer[offset++]=nibble2code[(b0>>>2)&0x3f];
buffer[offset++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
buffer[offset++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
buffer[offset++]=nibble2code[b2&077];
}
switch(remaining)
{
case 0:
break;
case 1:
if(chunks == 0)
offset = session.flush(buffer, lb.start, offset-lb.start);
b0=input[inOffset++];
buffer[offset++]=nibble2code[(b0>>>2)&0x3f];
buffer[offset++]=nibble2code[(b0<<4)&0x3f];
buffer[offset++]=pad;
buffer[offset++]=pad;
break;
case 2:
if(chunks == 0)
offset = session.flush(buffer, lb.start, offset-lb.start);
b0=input[inOffset++];
b1=input[inOffset++];
buffer[offset++]=nibble2code[(b0>>>2)&0x3f];
buffer[offset++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
buffer[offset++]=nibble2code[(b1<<2)&0x3f];
buffer[offset++]=pad;
break;
default:
throw new IllegalStateException("should not happen");
}
lb.offset = offset;
return lb;
}
encode(input, inOffset, inLen, lb.buffer, lb.offset, nibble2code, pad);
lb.offset += outputSize;
return lb;
}
/**
* Fast Base 64 decode as described in RFC 1421.
*/
public static byte[] decode(final byte[] b)
{
return decode(b, 0, b.length);
}
/**
* Fast Base 64 decode as described in RFC 1421.
*/
public static byte[] cdecode(final char[] b)
{
return cdecode(b, 0, b.length);
}
public static byte[] decode(final byte[] input, int inOffset, final int inLen)
{
return decode(input, inOffset, inLen, code2nibble, pad);
}
public static byte[] decodeUS(final byte[] input, int inOffset, final int inLen)
{
return decode(input, inOffset, inLen, code2nibbleUrlSafe, padUrlSafe);
}
/* ------------------------------------------------------------ */
/**
* Fast Base 64 decode as described in RFC 1421.
*
Does not attempt to cope with extra whitespace
* as described in RFC 1521.
*
Avoids creating extra copies of the input/output.
*
Note this code has been flattened for performance.
* @param input byte array to decode.
* @param inOffset the offset.
* @param inLen the length.
* @return byte array containing the decoded form of the input.
* @throws IllegalArgumentException if the input is not a valid
* B64 encoding.
*/
public static byte[] decode(final byte[] input, int inOffset, final int inLen,
byte[] code2nibble, byte pad)
{
if(inLen == 0)
return ByteString.EMPTY_BYTE_ARRAY;
if (inLen%4!=0)
throw new IllegalArgumentException("Input block size is not 4");
int withoutPaddingLen = inLen, limit = inOffset + inLen;
while (input[--limit]==pad)
withoutPaddingLen--;
// Create result array of exact required size.
final int outLen=((withoutPaddingLen)*3)/4;
final byte[] output=new byte[outLen];
decode(input, inOffset, inLen, output, 0, outLen, code2nibble, pad);
return output;
}
public static byte[] cdecode(final char[] input, int inOffset, final int inLen)
{
return cdecode(input, inOffset, inLen, code2nibble, pad);
}
public static byte[] cdecodeUS(final char[] input, int inOffset, final int inLen)
{
return cdecode(input, inOffset, inLen, code2nibbleUrlSafe, padUrlSafe);
}
/**
* Fast Base 64 decode as described in RFC 1421.
*
Does not attempt to cope with extra whitespace
* as described in RFC 1521.
*
Avoids creating extra copies of the input/output.
*
Note this code has been flattened for performance.
* @param input char array to decode.
* @param inOffset the offset.
* @param inLen the length.
* @return byte array containing the decoded form of the input.
* @throws IllegalArgumentException if the input is not a valid
* B64 encoding.
*/
public static byte[] cdecode(final char[] input, int inOffset, final int inLen,
byte[] code2nibble, byte pad)
{
if(inLen == 0)
return ByteString.EMPTY_BYTE_ARRAY;
if (inLen%4!=0)
throw new IllegalArgumentException("Input block size is not 4");
int withoutPaddingLen = inLen, limit = inOffset + inLen;
while (input[--limit]==pad)
withoutPaddingLen--;
// Create result array of exact required size.
final int outLen=((withoutPaddingLen)*3)/4;
final byte[] output=new byte[outLen];
cdecode(input, inOffset, inLen, output, 0, outLen, code2nibble, pad);
return output;
}
/**
* Returns the length of the decoded base64 input (written to the
* provided {@code output} byte array).
* The {@code output} byte array must have enough capacity or it will fail.
*/
public static int decodeTo(final byte[] output, int outOffset,
final byte[] input, int inOffset, final int inLen,
byte[] code2nibble, byte pad)
{
if(inLen == 0)
return 0;
if (inLen%4!=0)
throw new IllegalArgumentException("Input block size is not 4");
int withoutPaddingLen = inLen, limit = inOffset + inLen;
while (input[--limit]==pad)
withoutPaddingLen--;
// Create result array of exact required size.
final int outLen=((withoutPaddingLen)*3)/4;
assert (output.length - outOffset) >= outLen;
decode(input, inOffset, inLen, output, outOffset, outLen, code2nibble, pad);
return outLen;
}
private static void decode(final byte[] input, int inOffset, final int inLen,
final byte[] output, int outOffset, final int outLen,
byte[] code2nibble, byte pad)
{
int stop=(outLen/3)*3;
byte b0,b1,b2,b3;
try
{
while (outOffset>>4);
output[outOffset++]=(byte)(b1<<4|b2>>>2);
output[outOffset++]=(byte)(b2<<6|b3);
}
if (outLen!=outOffset)
{
switch (outLen%3)
{
case 0:
break;
case 1:
b0=code2nibble[input[inOffset++]];
b1=code2nibble[input[inOffset++]];
if (b0<0 || b1<0)
throw new IllegalArgumentException("Not B64 encoded");
output[outOffset++]=(byte)(b0<<2|b1>>>4);
break;
case 2:
b0=code2nibble[input[inOffset++]];
b1=code2nibble[input[inOffset++]];
b2=code2nibble[input[inOffset++]];
if (b0<0 || b1<0 || b2<0)
throw new IllegalArgumentException("Not B64 encoded");
output[outOffset++]=(byte)(b0<<2|b1>>>4);
output[outOffset++]=(byte)(b1<<4|b2>>>2);
break;
default:
throw new IllegalStateException("should not happen");
}
}
}
catch (IndexOutOfBoundsException e)
{
throw new IllegalArgumentException("char "+inOffset
+" was not B64 encoded");
}
}
private static void cdecode(final char[] input, int inOffset, final int inLen,
final byte[] output, int outOffset, final int outLen,
byte[] code2nibble, byte pad)
{
int stop=(outLen/3)*3;
byte b0,b1,b2,b3;
try
{
while (outOffset>>4);
output[outOffset++]=(byte)(b1<<4|b2>>>2);
output[outOffset++]=(byte)(b2<<6|b3);
}
if (outLen!=outOffset)
{
switch (outLen%3)
{
case 0:
break;
case 1:
b0=code2nibble[input[inOffset++]];
b1=code2nibble[input[inOffset++]];
if (b0<0 || b1<0)
throw new IllegalArgumentException("Not B64 encoded");
output[outOffset++]=(byte)(b0<<2|b1>>>4);
break;
case 2:
b0=code2nibble[input[inOffset++]];
b1=code2nibble[input[inOffset++]];
b2=code2nibble[input[inOffset++]];
if (b0<0 || b1<0 || b2<0)
throw new IllegalArgumentException("Not B64 encoded");
output[outOffset++]=(byte)(b0<<2|b1>>>4);
output[outOffset++]=(byte)(b1<<4|b2>>>2);
break;
default:
throw new IllegalStateException("should not happen");
}
}
}
catch (IndexOutOfBoundsException e)
{
throw new IllegalArgumentException("char "+inOffset
+" was not B64 encoded");
}
}
/**
* Returns the base 64 decoded bytes.
* The provided {@code str} must already be base-64 encoded.
*/
public static byte[] decode(final String str)
{
return decode(str, 0, str.length());
}
public static byte[] decode(final String str, int inOffset, final int inLen)
{
return decode(str, inOffset, inLen, code2nibble, pad);
}
public static byte[] decodeUS(final String str, int inOffset, final int inLen)
{
return decode(str, inOffset, inLen, code2nibbleUrlSafe, padUrlSafe);
}
/**
* Returns the base 64 decoded bytes.
* The provided {@code str} must already be base-64 encoded.
*/
public static byte[] decode(final String str, int inOffset, final int inLen,
byte[] code2nibble, byte pad)
{
if(inLen == 0)
return new byte[0];
if (inLen%4!=0)
throw new IllegalArgumentException("Input block size is not 4");
int withoutPaddingLen = inLen, limit = inOffset + inLen;
while (str.charAt(--limit)==pad)
withoutPaddingLen--;
// Create result array of exact required size.
final int outLen=((withoutPaddingLen)*3)/4;
final byte[] output=new byte[outLen];
decode(str, inOffset, inLen, output, 0, outLen, code2nibble, pad);
return output;
}
/**
* Returns the length of the decoded base64 input (written to the
* provided {@code output} byte array).
* The {@code output} byte array must have enough capacity or it will fail.
*/
public static int decodeTo(final byte[] output, int outOffset,
final String str, int inOffset, final int inLen,
byte[] code2nibble, byte pad)
{
if(inLen == 0)
return 0;
if (inLen%4!=0)
throw new IllegalArgumentException("Input block size is not 4");
int withoutPaddingLen = inLen, limit = inOffset + inLen;
while (str.charAt(--limit)==pad)
withoutPaddingLen--;
// Create result array of exact required size.
final int outLen=((withoutPaddingLen)*3)/4;
assert (output.length - outOffset) >= outLen;
decode(str, inOffset, inLen, output, outOffset, outLen,
code2nibble, pad);
return outLen;
}
private static void decode(final String str, int inOffset, final int inLen,
final byte[] output, int outOffset, final int outLen,
byte[] code2nibble, byte pad)
{
int stop=(outLen/3)*3;
byte b0,b1,b2,b3;
try
{
while (outOffset>>4);
output[outOffset++]=(byte)(b1<<4|b2>>>2);
output[outOffset++]=(byte)(b2<<6|b3);
}
if (outLen!=outOffset)
{
switch (outLen%3)
{
case 0:
break;
case 1:
b0=code2nibble[str.charAt(inOffset++)];
b1=code2nibble[str.charAt(inOffset++)];
if (b0<0 || b1<0)
throw new IllegalArgumentException("Not B64 encoded");
output[outOffset++]=(byte)(b0<<2|b1>>>4);
break;
case 2:
b0=code2nibble[str.charAt(inOffset++)];
b1=code2nibble[str.charAt(inOffset++)];
b2=code2nibble[str.charAt(inOffset++)];
if (b0<0 || b1<0 || b2<0)
throw new IllegalArgumentException("Not B64 encoded");
output[outOffset++]=(byte)(b0<<2|b1>>>4);
output[outOffset++]=(byte)(b1<<4|b2>>>2);
break;
default:
throw new IllegalStateException("should not happen");
}
}
}
catch (IndexOutOfBoundsException e)
{
throw new IllegalArgumentException("char "+inOffset
+" was not B64 encoded");
}
}
}