All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy