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}