001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2011 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038 039package org.dcm4che3.util; 040 041import java.io.ByteArrayOutputStream; 042import java.io.IOException; 043import java.io.OutputStream; 044 045/** 046 * @author Gunter Zeilinger <gunterze@gmail.com> 047 */ 048public class Base64 { 049 050 private static final char[] BASE64 = { 051 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 052 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 053 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 054 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 055 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; 056 057 private static final byte INV_BASE64[] = { 058 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 059 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 060 -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 061 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 062 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 063 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 064 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 065 }; 066 067 public static void encode(byte[] src, int srcPos, int srcLen, char[] dest, 068 int destPos) { 069 if (srcPos < 0 || srcLen < 0 || srcLen > src.length - srcPos) 070 throw new IndexOutOfBoundsException(); 071 int destLen = (srcLen * 4 / 3 + 3) & ~3; 072 if (destPos < 0 || destLen > dest.length - destPos) 073 throw new IndexOutOfBoundsException(); 074 byte b1, b2, b3; 075 int n = srcLen / 3; 076 int r = srcLen - 3 * n; 077 while (n-- > 0) { 078 dest[destPos++] = BASE64[((b1 = src[srcPos++]) >>> 2) & 0x3F]; 079 dest[destPos++] = BASE64[((b1 & 0x03) << 4) 080 | (((b2 = src[srcPos++]) >>> 4) & 0x0F)]; 081 dest[destPos++] = BASE64[((b2 & 0x0F) << 2) 082 | (((b3 = src[srcPos++]) >>> 6) & 0x03)]; 083 dest[destPos++] = BASE64[b3 & 0x3F]; 084 } 085 if (r > 0) 086 if (r == 1) { 087 dest[destPos++] = BASE64[((b1 = src[srcPos]) >>> 2) & 0x3F]; 088 dest[destPos++] = BASE64[((b1 & 0x03) << 4)]; 089 dest[destPos++] = '='; 090 dest[destPos++] = '='; 091 } else { 092 dest[destPos++] = BASE64[((b1 = src[srcPos++]) >>> 2) & 0x3F]; 093 dest[destPos++] = BASE64[((b1 & 0x03) << 4) 094 | (((b2 = src[srcPos]) >>> 4) & 0x0F)]; 095 dest[destPos++] = BASE64[(b2 & 0x0F) << 2]; 096 dest[destPos++] = '='; 097 } 098 } 099 100 public static void decode(char[] ch, int off, int len, OutputStream out) 101 throws IOException { 102 byte b2, b3; 103 while ((len -= 2) >= 0) { 104 out.write((byte)((INV_BASE64[ch[off++]] << 2) 105 | ((b2 = INV_BASE64[ch[off++]]) >>> 4))); 106 if ((len-- == 0) || ch[off] == '=') 107 break; 108 out.write((byte)((b2 << 4) 109 | ((b3 = INV_BASE64[ch[off++]]) >>> 2))); 110 if ((len-- == 0) || ch[off] == '=') 111 break; 112 out.write((byte)((b3 << 6) | INV_BASE64[ch[off++]])); 113 } 114 } 115 116 /** 117 * Convenience method. To achieve best performance, use Base64.encode 118 * @param bytes data to encode 119 * @return Base64-encoded string 120 */ 121 public static String toBase64(byte[] bytes) { 122 final char[] encodedChars = new char[(bytes.length * 4 / 3 + 3) & ~3]; 123 encode(bytes, 0, bytes.length, encodedChars, 0); 124 return new String(encodedChars); 125 } 126 127 /** 128 * Convenience method. To achieve best performance, use Base64.decode 129 * @param base64String encoded string 130 * @return Decoded data 131 * @throws IOException 132 */ 133 public static byte[] fromBase64(String base64String) throws IOException { 134 final int encodedLength = base64String.length(); 135 final int decodedLength = encodedLength * 3 / 4; 136 137 final char[] encodedChars = new char[encodedLength]; 138 base64String.getChars(0, encodedLength, encodedChars, 0); 139 140 final ByteArrayOutputStream stream = new ByteArrayOutputStream(decodedLength); 141 decode(encodedChars,0, encodedLength,stream); 142 143 return stream.toByteArray(); 144 } 145 146}