/** * Copyright 2007-2015, Kaazing Corporation. All rights reserved. * * 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.kaazing.specification.wse.internal; /** * Internal class. This class manages the Base64 encoding and decoding */ final class Base64 { private static final byte[] INDEXED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" .getBytes(); private static final byte PADDING_BYTE = (byte) '='; public static byte[] encode(byte[] decodedArray) { int decodedSize = decodedArray.length; int effectiveDecodedSize = ((decodedSize + 2) / 3) * 3; int decodedFragmentSize = decodedSize % 3; int encodedArraySize = effectiveDecodedSize / 3 * 4; byte[] encodedArray = new byte[encodedArraySize]; int encodedArrayPosition = 0; int decodedArrayOffset = 0; int decodedArrayPosition = decodedArrayOffset + 0; int decodedArrayLimit = decodedArrayOffset + decodedArray.length - decodedFragmentSize; while (decodedArrayPosition < decodedArrayLimit) { int byte0 = decodedArray[decodedArrayPosition++] & 0xff; int byte1 = decodedArray[decodedArrayPosition++] & 0xff; int byte2 = decodedArray[decodedArrayPosition++] & 0xff; encodedArray[encodedArrayPosition++] = INDEXED[(byte0 >> 2) & 0x3f]; encodedArray[encodedArrayPosition++] = INDEXED[((byte0 << 4) & 0x30) | ((byte1 >> 4) & 0x0f)]; encodedArray[encodedArrayPosition++] = INDEXED[((byte1 << 2) & 0x3c) | ((byte2 >> 6) & 0x03)]; encodedArray[encodedArrayPosition++] = INDEXED[byte2 & 0x3f]; } if (decodedFragmentSize == 1) { int byte0 = decodedArray[decodedArrayPosition++] & 0xff; encodedArray[encodedArrayPosition++] = INDEXED[(byte0 >> 2) & 0x3f]; encodedArray[encodedArrayPosition++] = INDEXED[(byte0 << 4) & 0x30]; encodedArray[encodedArrayPosition++] = PADDING_BYTE; encodedArray[encodedArrayPosition++] = PADDING_BYTE; } else if (decodedFragmentSize == 2) { int byte0 = decodedArray[decodedArrayPosition++] & 0xff; int byte1 = decodedArray[decodedArrayPosition++] & 0xff; encodedArray[encodedArrayPosition++] = INDEXED[(byte0 >> 2) & 0x3f]; encodedArray[encodedArrayPosition++] = INDEXED[((byte0 << 4) & 0x30) | ((byte1 >> 4) & 0x0f)]; encodedArray[encodedArrayPosition++] = INDEXED[(byte1 << 2) & 0x3c]; encodedArray[encodedArrayPosition++] = PADDING_BYTE; } return encodedArray; } public static byte[] decode(byte[] encodedArray) { if (encodedArray == null) { return null; } int length = encodedArray.length; if (length % 4 != 0) { throw new IllegalArgumentException("Invalid Base64 Encoded String"); } byte[] decodedArray = new byte[length / 4 * 3]; int decodedArrayOffset = 0; int i = 0; while (i < length) { int char0 = encodedArray[i++]; int char1 = encodedArray[i++]; int char2 = encodedArray[i++]; int char3 = encodedArray[i++]; int byte0 = mapped(char0); int byte1 = mapped(char1); int byte2 = mapped(char2); int byte3 = mapped(char3); decodedArray[decodedArrayOffset++] = (byte) (((byte0 << 2) & 0xfc) | ((byte1 >> 4) & 0x03)); if (char2 != PADDING_BYTE) { decodedArray[decodedArrayOffset++] = (byte) (((byte1 << 4) & 0xf0) | ((byte2 >> 2) & 0x0f)); if (char3 != PADDING_BYTE) { decodedArray[decodedArrayOffset++] = (byte) (((byte2 << 6) & 0xc0) | (byte3 & 0x3f)); } } } assert decodedArrayOffset == decodedArray.length; return decodedArray; } private static int mapped(int ch) { if ((ch & 0x40) != 0) { if ((ch & 0x20) != 0) { // a(01100001)-z(01111010) -> 26-51 assert ch >= 'a'; assert ch <= 'z'; return ch - 71; } else { // A(01000001)-Z(01011010) -> 0-25 assert ch >= 'A'; assert ch <= 'Z'; return ch - 65; } } else if ((ch & 0x20) != 0) { if ((ch & 0x10) != 0) { if ((ch & 0x08) != 0 && (ch & 0x04) != 0) { // =(00111101) -> 0 assert ch == '='; return 0; } else { // 0(00110000)-9(00111001) -> 52-61 assert ch >= '0'; assert ch <= '9'; return ch + 4; } } else { if ((ch & 0x04) != 0) { // /(00101111) -> 63 assert ch == '/'; return 63; } else { // +(00101011) -> 62 assert ch == '+'; return 62; } } } else { throw new IllegalArgumentException("Invalid BASE64 string"); } } private Base64() { // utility } }